2008-09-10 5 views
11

Ich möchte die Ergebnisse von zwei Linq Expressions komponieren. Sie existieren in FormWie komponiere ich bestehende Linq Expressions

Expression<Func<T, bool>> 

So die beiden, die ich komponieren wollen im Wesentlichen Delegierten auf einem Parameter (vom Typ T), die beide einen boolean zurück. Das Ergebnis, das ich zusammengestellt haben möchte, wäre die logische Auswertung der Booleschen Werte. Ich würde es wahrscheinlich als eine Erweiterung Methode implementieren, um meine Syntax etwas wie wäre:

Expression<Func<User, bool>> expression1 = t => t.Name == "steve"; 
Expression<Func<User, bool>> expression2 = t => t.Age == 28; 
Expression<Func<User, bool>> composedExpression = expression1.And(expression2); 

Und später in meinem Code ich den zusammengesetzten Ausdruck bewerten will

var user = new User(); 
bool evaluated = composedExpression.Compile().Invoke(user); 

Ich stieß habe um mit einem einige verschiedene Ideen, aber ich fürchte, dass es komplexer ist, als ich es mir erhofft hatte. Wie wird das gemacht?

Antwort

17

ein Beispiel:

var user1 = new User {Name = "steve", Age = 28}; 
var user2 = new User {Name = "foobar", Age = 28}; 

Expression<Func<User, bool>> expression1 = t => t.Name == "steve"; 
Expression<Func<User, bool>> expression2 = t => t.Age == 28; 

var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>()); 

var result = Expression.Lambda<Func<User, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters); 

Console.WriteLine(result.Compile().Invoke(user1)); // true 
Console.WriteLine(result.Compile().Invoke(user2)); // false 

Sie diesen Code über Erweiterungsmethoden wiederverwenden können:

class User 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 
} 

public static class PredicateExtensions 
{ 
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1,Expression<Func<T, bool>> expression2) 
    { 
    InvocationExpression invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>()); 

    return Expression.Lambda<Func<T, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
    var user1 = new User {Name = "steve", Age = 28}; 
    var user2 = new User {Name = "foobar", Age = 28}; 

    Expression<Func<User, bool>> expression1 = t => t.Name == "steve"; 
    Expression<Func<User, bool>> expression2 = t => t.Age == 28; 

    var result = expression1.And(expression2); 

    Console.WriteLine(result.Compile().Invoke(user1)); 
    Console.WriteLine(result.Compile().Invoke(user2)); 
    } 
} 
1

Warum verwenden Sie nicht einfach und befassen sich mit dem resultierenden BinaryExpression?

Expression<Func<T, bool>> expr1 = t => t.Name == "steve"; 
Expression<Func<T, bool>> expr2 = t => t.Age == 28; 
Expression composed = Expression.And(expr1.Body, expr2.Body); 

Sie können dies natürlich bündeln in einen Lambda Ihre gewünschte Signatur zu erhalten, aber das ist teuer und sollte nur einmal, nicht mehrfach durchgeführt werden:

Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(
    expr, Expression.Parameter(typeof(T), "t") 
); 

/EDIT: Sie können natürlich lambdas wie folgt kombinieren, aber dies beinhaltet redundante Compilations und Funktionsaufrufe:

Expression<Func<string, bool>> z = t => expr1.Compile()(t) && expr2.Compile()(t); 

Verdammt, Wartung Ausfallzeiten. Musste den ganzen Beitrag nochmal tippen. : -/

/EDIT: aku ist richtig, obwohl. Sie müssen expr2 separat aufrufen, sonst findet der Compiler die Parameterreferenz nicht. Hier

Verwandte Themen