2009-07-15 23 views
11

我想合并下面的表达式:如何在不调用的情况下合并两个C#Lambda表达式?

// 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; 

我把它用在一个selectOrderLines调用,并将结果提供给validateOrderLines工作,但因为我使用实体框架中这些表达式,我必须创建一个干净的表达,它应该代表:

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

我该怎么做?

回答

19

最优雅的方式是使用Expression Visitor。特别是,这个MSDN Blog Entry描述了如何使用它来组合谓词(使用布尔型And或Or)而没有Invoke。

EDITED已经认识布尔组合是不是你想要的,我写ExpressionVisitor的样品使用,解决您的特定问题:

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

该扩建工程:

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); 
     } 
    } 

样品使用:

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

我需要什么给我n为了将括号添加到此? 我的意思是如果我想建立一个像 (ProductId == 1或ProductId == 2)和a.text.StartsWith(“a”)的过滤器 – Marty 2012-08-13 09:20:11

相关问题