2009-07-15 30 views
11

tôi muốn hợp nhất các biểu thức sau:Làm cách nào để hợp nhất hai Biểu thức Lambda C# không có lời gọi?

// example class 
class Order 
{ 
    List<OrderLine> Lines  
} 
class OrderLine { } 

Expression<Func<Order, List<OrderLine>>> selectOrderLines = o => o.Lines; 
Expression<Func<List<OrderLine>, Boolean>> validateOrderLines = lines => lines.Count > 0; 

// now combine those to 
Expression<Func<Order, Boolean>> validateOrder; 

tôi đã nhận nó để làm việc bằng cách sử dụng invoke trên selectOrderLines và cung cấp kết quả cho validateOrderLines, nhưng vì tôi đang sử dụng các biểu thức trong Entity Framework, Tôi phải thực sự tạo ra một biểu thức rõ ràng cần đại diện:

Expression<Func<Order, Boolean>> validateOrder = o => o.Lines.Count > 0; 

Tôi làm cách nào để thực hiện điều này?

Trả lời

19

Cách thanh lịch nhất là sử dụng Expression Visitor. Cụ thể, điều này MSDN Blog Entry mô tả cách sử dụng nó để kết hợp các vị từ (sử dụng boolean và hoặc Or) mà không cần gọi.

EDITED Có nhận ra sự kết hợp boolean không phải là những gì bạn muốn, tôi đã viết một cách sử dụng mẫu ExpressionVisitor mà giải quyết cho vấn đề cụ thể của bạn:

public class ParameterToMemberExpressionRebinder : ExpressionVisitor 
{ 
    ParameterExpression _paramExpr; 
    MemberExpression _memberExpr; 

    ParameterToMemberExpressionRebinder(ParameterExpression paramExpr, MemberExpression memberExpr) 
    { 
     _paramExpr = paramExpr; 
     _memberExpr = memberExpr; 
    } 

    protected override Expression Visit(Expression p) 
    { 
     return base.Visit(p == _paramExpr ? _memberExpr : p); 
    } 

    public static Expression<Func<T, bool>> CombinePropertySelectorWithPredicate<T, T2>(
     Expression<Func<T, T2>> propertySelector, 
     Expression<Func<T2, bool>> propertyPredicate) 
    { 
     var memberExpression = propertySelector.Body as MemberExpression; 

     if (memberExpression == null) 
     { 
      throw new ArgumentException("propertySelector"); 
     } 

     var expr = Expression.Lambda<Func<T, bool>>(propertyPredicate.Body, propertySelector.Parameters); 
     var rebinder = new ParameterToMemberExpressionRebinder(propertyPredicate.Parameters[0], memberExpression); 
     expr = (Expression<Func<T, bool>>)rebinder.Visit(expr); 

     return expr; 
    } 

    class OrderLine 
    { 
    } 

    class Order 
    { 
     public List<OrderLine> Lines; 
    } 

    static void test() 
    { 
     Expression<Func<Order, List<OrderLine>>> selectOrderLines = o => o.Lines; 
     Expression<Func<List<OrderLine>, Boolean>> validateOrderLines = lines => lines.Count > 0; 
     var validateOrder = ParameterToMemberExpressionRebinder.CombinePropertySelectorWithPredicate(selectOrderLines, validateOrderLines); 

     // validateOrder: {o => (o.Lines.Count > 0)} 
    } 
} 
3

Phần mở rộng này hoạt động:

public static class Utility 
    { 
     public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) 
     { 
      // build parameter map (from parameters of second to parameters of first) 
      var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); 

      // replace parameters in the second lambda expression with parameters from the first 
      var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); 

      // apply composition of lambda expression bodies to parameters from the first expression 
      return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); 
     } 

     public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) 
     { 
      return first.Compose(second, Expression.And); 
     } 

     public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) 
     { 
      return first.Compose(second, Expression.Or); 
     } 
    } 

Sample sử dụng:

Expression<Func<Product, bool>> filter1 = p => a.ProductId == 1; 
Expression<Func<Product, bool>> filter2 = p => a.Text.StartWith("test"); 
Expression<Func<Product, bool>> filterCombined = filter1.And(filter2); 
+0

Tôi cần gì để tôi n để thêm dấu ngoặc vào cái này? Ý tôi là nếu tôi muốn xây dựng bộ lọc như (ProductId == 1 hoặc ProductId == 2) và a.text.StartsWith ("a") – Marty

+1

Tôi tìm thông số ở đâu? – h8red

+2

Nếu vì lý do nào đó bạn vẫn đang tìm tham sốRebinder, nó có thể được tìm thấy tại [MSDN blog] (http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities -combining-predicates.aspx) –

Các vấn đề liên quan