2009-07-15 8 views
11

私は、次の式をマージしたい:、私はそれがselectOrderLinesに呼び出しを使用して動作するようになったとvalidateOrderLinesに結果を供給呼び出しを行わずに2つのC#ラムダ式をマージする方法はありますか?

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

が、私はEntity Frameworkの中でこれらの式を使用していますので、私は実際に表現する必要があるきれいな表情を作成しなければなりません:

どうすればいいですか?

答えて

19

最も洗練された方法はExpression Visitorです。特に、このMSDN Blog Entryは、Invokeを使用せずに(ブール値のAndまたはOrを使用して)述語を結合する方法を記述しています。

ブール組み合わせは、あなたが望んだものではありません、私はあなたの特定の問題のために解決しExpressionVisitorの使用例を書いたEDITEDを実現した:

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

私に何が必要なのですか?これに角括弧を追加するには? つまり、 (ProductId == 1またはProductId == 2)やa.text.StartsWith( "a")のようなフィルタを作成したい場合は、 – Marty

+1

ここでparameterRebinderはありますか? – h8red

+2

何らかの理由でparameterRebinderを探している場合は、[MSDN blog](http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities)にあります。 -combining-predicates.aspx) –

関連する問題