Coder Perfect

Two expressions (ExpressionFuncT, bool>>) are combined.

Problem

I have two ExpressionFuncT, bool>> expressions that I want to OR, AND, or NOT in order to get a new expression of the same type.

Expression<Func<T, bool>> expr1;
Expression<Func<T, bool>> expr2;

...

//how to do this (the code below will obviously not work)
Expression<Func<T, bool>> andExpression = expr AND expr2

Asked by BjartN

Solution #1

You can, however, utilize Expression. To combine logical expressions, you can use AndAlso, OrElse, and other operators, but the problem is the parameters; are you using the same parameterExpression in expr1 and expr2? If that’s the case, it’s a lot easier:

var body = Expression.AndAlso(expr1.Body, expr2.Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, expr1.Parameters[0]);

This is also useful for negating a single operation:

static Expression<Func<T, bool>> Not<T>(
    this Expression<Func<T, bool>> expr)
{
    return Expression.Lambda<Func<T, bool>>(
        Expression.Not(expr.Body), expr.Parameters[0]);
}

Otherwise, you might be able to combine them with Invoke, depending on the LINQ provider:

// OrElse is very similar...
static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> left,
    Expression<Func<T, bool>> right)
{
    var param = Expression.Parameter(typeof(T), "x");
    var body = Expression.AndAlso(
            Expression.Invoke(left, param),
            Expression.Invoke(right, param)
        );
    var lambda = Expression.Lambda<Func<T, bool>>(body, param);
    return lambda;
}

Somewhere, I have got some code that re-writes an expression-tree replacing nodes to remove the need for Invoke, but it is quite lengthy (and I can’t remember where I left it…)

The following is a simplified version that chooses the simplest path:

static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> expr1,
    Expression<Func<T, bool>> expr2)
{
    // need to detect whether they use the same
    // parameter instance; if not, they need fixing
    ParameterExpression param = expr1.Parameters[0];
    if (ReferenceEquals(param, expr2.Parameters[0]))
    {
        // simple version
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(expr1.Body, expr2.Body), param);
    }
    // otherwise, keep expr1 "as is" and invoke expr2
    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(
            expr1.Body,
            Expression.Invoke(expr2, param)), param);
}

The ExpressionVisitor class, which was introduced in.NET 4.0, allows you to create EF-safe expressions.

    public static Expression<Func<T, bool>> AndAlso<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var parameter = Expression.Parameter(typeof (T));

        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
        var left = leftVisitor.Visit(expr1.Body);

        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
        var right = rightVisitor.Visit(expr2.Body);

        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(left, right), parameter);
    }



    private class ReplaceExpressionVisitor
        : ExpressionVisitor
    {
        private readonly Expression _oldValue;
        private readonly Expression _newValue;

        public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
        {
            _oldValue = oldValue;
            _newValue = newValue;
        }

        public override Expression Visit(Expression node)
        {
            if (node == _oldValue)
                return _newValue;
            return base.Visit(node);
        }
    }

Answered by Marc Gravell

Solution #2

You have the option of using Expression. To combine logical expressions, use AndAlso / OrElse, but make sure the parameterExpressions are the same.

I was having difficulties with EF and the PredicateBuilder, so I created my own that I could use instead of Invoke:

var filterC = filterA.And(filterb);

My PredicateBuilder source code is as follows:

public static class PredicateBuilder {

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }   
}

And here’s the utility class for substituting lambda parameters:

internal class SubstExpressionVisitor : System.Linq.Expressions.ExpressionVisitor {
        public Dictionary<Expression, Expression> subst = new Dictionary<Expression, Expression>();

        protected override Expression VisitParameter(ParameterExpression node) {
            Expression newValue;
            if (subst.TryGetValue(node, out newValue)) {
                return newValue;
            }
            return node;
        }
    }

Answered by Adam Tegen

Solution #3

If your provider doesn’t allow Invoke and you need to combine two expressions, you can use an ExpressionVisitor to replace the second expression’s parameter with the first expression’s parameter.

class ParameterUpdateVisitor : ExpressionVisitor
{
    private ParameterExpression _oldParameter;
    private ParameterExpression _newParameter;

    public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
    {
        _oldParameter = oldParameter;
        _newParameter = newParameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (object.ReferenceEquals(node, _oldParameter))
            return _newParameter;

        return base.VisitParameter(node);
    }
}

static Expression<Func<T, bool>> UpdateParameter<T>(
    Expression<Func<T, bool>> expr,
    ParameterExpression newParameter)
{
    var visitor = new ParameterUpdateVisitor(expr.Parameters[0], newParameter);
    var body = visitor.Visit(expr.Body);

    return Expression.Lambda<Func<T, bool>>(body, newParameter);
}

[TestMethod]
public void ExpressionText()
{
    string text = "test";

    Expression<Func<Coco, bool>> expr1 = p => p.Item1.Contains(text);
    Expression<Func<Coco, bool>> expr2 = q => q.Item2.Contains(text);
    Expression<Func<Coco, bool>> expr3 = UpdateParameter(expr2, expr1.Parameters[0]);

    var expr4 = Expression.Lambda<Func<Recording, bool>>(
        Expression.OrElse(expr1.Body, expr3.Body), expr1.Parameters[0]);

    var func = expr4.Compile();

    Assert.IsTrue(func(new Coco { Item1 = "caca", Item2 = "test pipi" }));
}

Answered by Francis

Solution #4

There’s nothing new here, but I’ve combined this answer with this answer and refactored it significantly so that even I can understand what’s going on:

public static class ExpressionExtensions
{
    public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        ParameterExpression parameter1 = expr1.Parameters[0];
        var visitor = new ReplaceParameterVisitor(expr2.Parameters[0], parameter1);
        var body2WithParam1 = visitor.Visit(expr2.Body);
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, body2WithParam1), parameter1);
    }

    private class ReplaceParameterVisitor : ExpressionVisitor
    {
        private ParameterExpression _oldParameter;
        private ParameterExpression _newParameter;

        public ReplaceParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
        {
            _oldParameter = oldParameter;
            _newParameter = newParameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (ReferenceEquals(node, _oldParameter))
                return _newParameter;

            return base.VisitParameter(node);
        }
    }
}

Answered by Dejan

Solution #5

I needed to obtain the same outcomes with a more generic method (as the type was not known). I finally understood out what I was trying to accomplish thanks to marc’s response:

    public static LambdaExpression CombineOr(Type sourceType, LambdaExpression exp, LambdaExpression newExp) 
    {
        var parameter = Expression.Parameter(sourceType);

        var leftVisitor = new ReplaceExpressionVisitor(exp.Parameters[0], parameter);
        var left = leftVisitor.Visit(exp.Body);

        var rightVisitor = new ReplaceExpressionVisitor(newExp.Parameters[0], parameter);
        var right = rightVisitor.Visit(newExp.Body);

        var delegateType = typeof(Func<,>).MakeGenericType(sourceType, typeof(bool));
        return Expression.Lambda(delegateType, Expression.Or(left, right), parameter);
    }

Answered by VorTechS

Post is based on https://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool