tags:

views:

2362

answers:

3

I want to compose the results of two Linq Expressions. They exist in the form

Expression<Func<T, bool>>

So the two that I want to compose are essentially delegates on a parameter (of type T) that both return a boolean. The result I would like composed would be the logical evaluation of the booleans. I would probably implement it as an extension method so my syntax would be something like:

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

And later on in my code I want to evaluate the composed expression

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

I have poked around with a few different ideas but I fear that it is more complex than I had hoped. How is this done?

A: 

Why don't you just use Expression.And and deal with the resulting 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);

You can of course bundle this into a lambda to get your desired signature but this is expensive and should only be done once, not multiple times:

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

/EDIT: You can of course combine lambdas as follows but this involves redundant compilations and function calls:

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

Damn, maintainance downtime. Had to type the whole post again. :-/

/EDIT: aku's right, though. You have to invoke expr2 separately, else the compiler won't find the parameter reference.

Konrad Rudolph
+6  A: 

Here is an example:

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

You can reuse this code via extension methods:

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));
    }
}
aku
+2  A: 

You can also use LinqKit, which can do all this work for You.Have a look at this link http://www.albahari.com/nutshell/linqkit.aspx