2009-07-15 6 views
11

Ich möchte die folgenden Ausdrücke fusionieren:Wie zwei C# Lambda-Ausdrücke ohne Aufruf zusammengeführt werden?

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

Ich habe es auf den selectOrderLines mit einem invoke zu arbeiten und das Ergebnis an den validateOrderLines Versorgung, sondern weil ich diese Ausdrücke in Entity Framework bin mit, Ich muss tatsächlich einen sauberen Ausdruck erstellen, der Folgendes darstellen sollte:

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

Wie kann ich das tun?

Antwort

19

Der eleganteste Weg ist die Verwendung eines Expression Visitor. Insbesondere beschreibt diese MSDN Blog Entry, wie es verwendet wird, um Prädikate zu kombinieren (mit Boolean And oder Or) ohne Invoke.

EDITED realisiert boolean Kombination zu haben, ist nicht das, was man wollte, schrieb ich eine Probe Verwendung von Expression, die für Ihr Problem löst:

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

Diese Erweiterung funktioniert:

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

Probe mit:

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

Was würde ich brauchen, um ich n um Klammern hinzuzufügen? Ich meine Wenn ich einen Filter wie (ProductId == 1 oder ProductId == 2) und a.text.StartsWith ("a") – Marty

+1

Wo finde ich den ParameterRebinder? – h8red

+2

Wenn Sie aus irgendeinem Grund noch nach ParameterRebinder suchen, finden Sie sie unter [MSDN blog] (http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities) -combining-Prädikate.aspx) –

Verwandte Themen