views:

686

answers:

2

I'd like to merge the following Expression:

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

I got it to work using a invoke on the selectOrderLines and supplying the result to the validateOrderLines, but because I'm using these expressions in Entity Framework, I have to actually create a clean expression three which should represent:

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

How can I do this?

A: 

I think this works;

Func<Order, List<OrderLine>> selectOrderLinesDelegate =
    selectOrderLines.Compile();
Func<List<OrderLine>, Boolean> validateOrderLinesDelegate =
    validateOrderLines.Compile();
Expression<Func<Order, Boolean>> combined =
    o => validateOrderLinesDelegate(selectOrderLinesDelegate(o));
David M
Since you are compiling the expressions (into code, away from expression trees), they will no longer be usable with the Entity Framework.
Ben M
But combined isn't compiled, and the other two are still available uncompiled as well. So all three expressions are available to use aren't they?
David M
Your final variable is a tree, but it invokes two compiled (as in code) functions, which are not.
Ben M
+4  A: 

The most elegant way is to use an Expression Visitor. In particular, this MSDN Blog Entry describes how to use it to combine predicates (using boolean And or Or) without Invoke.

EDITED Having realized boolean combination is not what you wanted, I wrote a sample usage of ExpressionVisitor that solves for your particular problem:

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)}
    }
}
Ben M