2012-02-10 9 views
11

Programsal olarak bir ifade ağacı oluşturmaya çalışıyorum. Benim girişi varOrElse ve AndAlso ifade yöntemlerini kullanarak özel durum

aşağıdaki formu var koşul sınıfları listesi: Ben Expression nesneyi oluştururken

public class Filter 
{ 
    public string field { get; set; } 
    public string operator { get; set; } 
    public string value { get; set; } 
} 

aşağıdaki şekilde

foreach (Filter sf in rules) { 
    Expression ex = sf.ToExpression(query); 
    if (mainExpression == null) { 
     mainExpression = ex; 
    } 
    else { 
     if (logicalCondition == "AND") { 
      mainExpression = Expression.And(mainExpression, ex); 
     } 
     else if (logicalCondition == "OR") { 
      mainExpression = Expression.Or(mainExpression, ex); 
     } 
    } 
} 
her durum için bir Expression oluşturmak

Filter.ToExpression() yöntemi, bu

public override Expression ToExpression(IQueryable query) { 
    ParameterExpression parameter = Expression.Parameter(query.ElementType, "p"); 
    MemberExpression memberAccess = null; 
    foreach (var property in field.Split('.')) 
     memberAccess = MemberExpression.Property(memberAccess ?? (parameter as Expression), property); 
    ConstantExpression filter = Expression.Constant(Convert.ChangeType(value, memberAccess.Type)); 
    WhereOperation condition = (WhereOperation)StringEnum.Parse(typeof(WhereOperation), operator); 
    LambdaExpression lambda = BuildLambdaExpression(memberAccess, filter, parameter, condition, value); 
    return lambda; 
} 
gibi uygulanan

ikili operatör Veya tanımlanmadı: Tek bir şartım var ama And, Or, AndAlso, OrElse statik yöntemlerden birini kullanarak ifadeleri birleştirmek çalıştığımda diyor bir InvalidOperationException aldığınızda

Her şey çalışıyor 'System.Func 2[MyObject,System.Boolean]' and 'System.Func 2 [MyObject, System.Boolean]' türleri için.

Biraz kafam karıştı. Birisi istisnanın nedenlerini daha iyi açıklayabilir ve bir çözüm önerebilir mi?

Çok teşekkürler!

cevap

23
Sen (a => a == 3) || (a => a == 4) içine a => a == 3 ve a => a == 4 birleştirerek yapıyor ancak bunun yerine a => (a == 3 || a == 4) yapmaya çalışıyorum gerektiğini

. Bu, elle yapmak için çok zor değil, ancak someone has done it for you already. "İfadeleri Birleştir" konusuna bakın.

Düzenle: istendiği gibi, bunun nasıl yapılacağı hakkında basit bir örnek.

Düzenleme 2: o .NET 4, ancak at MSDN you can find a usable implementation for earlier versions yenidir ExpressionVisitor kullanır. MSDN kodunun sizin amaçlarınız için "üçüncü taraf" olarak nitelendirilmediğini varsayalım. Yalnızca protected virtual Expression Visit(Expression exp) yöntemini public olarak değiştirmeniz gerekir. Ve Enumerable.Zip sizin için uygun değil ve gerekli değil, artık gitti.

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Linq.Expressions; 

namespace DemoApp 
{ 
    <include ExpressionVisitor definition here for .NET 3.5> 

    public class ExpressionParameterReplacer : ExpressionVisitor 
    { 
     public ExpressionParameterReplacer(IList<ParameterExpression> fromParameters, IList<ParameterExpression> toParameters) 
     { 
      ParameterReplacements = new Dictionary<ParameterExpression, ParameterExpression>(); 
      for (int i = 0; i != fromParameters.Count && i != toParameters.Count; i++) 
       ParameterReplacements.Add(fromParameters[i], toParameters[i]); 
     } 
     private IDictionary<ParameterExpression, ParameterExpression> ParameterReplacements 
     { 
      get; 
      set; 
     } 
     protected override Expression VisitParameter(ParameterExpression node) 
     { 
      ParameterExpression replacement; 
      if (ParameterReplacements.TryGetValue(node, out replacement)) 
       node = replacement; 
      return base.VisitParameter(node); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Expression<Func<int, bool>> exprA = a => a == 3; 
      Expression<Func<int, bool>> exprB = b => b == 4; 
      Expression<Func<int, bool>> exprC = 
       Expression.Lambda<Func<int, bool>>(
        Expression.OrElse(
         exprA.Body, 
         new ExpressionParameterReplacer(exprB.Parameters, exprA.Parameters).Visit(exprB.Body)), 
        exprA.Parameters); 
      Console.WriteLine(exprA.ToString()); 
      Console.WriteLine(exprB.ToString()); 
      Console.WriteLine(exprC.ToString()); 
      Func<int, bool> funcA = exprA.Compile(); 
      Func<int, bool> funcB = exprB.Compile(); 
      Func<int, bool> funcC = exprC.Compile(); 
      Debug.Assert(funcA(3) && !funcA(4) && !funcA(5)); 
      Debug.Assert(!funcB(3) && funcB(4) && !funcB(5)); 
      Debug.Assert(funcC(3) && funcC(4) && !funcC(5)); 
     } 
    } 
} 
+0

Merhaba, cevabınız için teşekkürler. Bu sorunu çözmek için üçüncü taraf kodunu kullanamıyorum. Bunu el ile yapmanın yolu hangisi daha iyi olur? Tekrar teşekkürler! – Lorenzo

+0

@Lorenzo Sure, örnek olarak kullandığım iki ifadeye dayalı bir program ekledim. – hvd

+0

Merhaba, çözümünüzü uygulamaya çalıştım. “ExpressionVisitor” un LinqKit kaynaklarından geldiğini ve nasıl çalıştığını görebildiğini anladım. Şimdi soru şudur: IEnumerable '' Zip' yönteminden nereden geliyor? .NET 3.5 kullanıyorum ve şu yöntemi bulamıyorum :( – Lorenzo