Combining two expressions (Expression<Func<T, bool>>)

C#LinqLambdaExpression

C# Problem Overview


I have two expressions of type Expression<Func<T, bool>> and I want to take to OR, AND or NOT of these and 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

C# Solutions


Solution 1 - C#

Well, you can use Expression.AndAlso / OrElse etc to combine logical expressions, but the problem is the parameters; are you working with the same ParameterExpression in expr1 and expr2? If so, it is easier:

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

This also works well to negate 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, depending on the LINQ provider, you might be able to combine them with Invoke:

// 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...)


Generalized version that picks the simplest route:

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

Starting from .NET 4.0, there is the ExpressionVisitor class which allows you to build expressions that are EF safe.

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

Solution 2 - C#

You can use Expression.AndAlso / OrElse to combine logical expressions, but you have to make sure the ParameterExpressions are the same.

I was having trouble with EF and the PredicateBuilder so I made my own without resorting to Invoke, that I could use like this:

var filterC = filterA.And(filterb);

Source code for my PredicateBuilder:

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 the utility class to substitute the parameters in a lambda:

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

Solution 3 - C#

If you provider does not support Invoke and you need to combine two expression, you can use an ExpressionVisitor to replace the parameter in the second expression by the parameter in the first expression.

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

Solution 4 - C#

Nothing new here but married this answer with this answer and slightly refactored it so that even I 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);
        }
    }
}

Solution 5 - C#

I combined some beautiful answers here to make it possible to easily support more Expression operators.

This is based on the answer of @Dejan but now it's quite easy to add the OR as well. I chose not to make the Combine function public, but you could do that to be even more flexible.

public static class ExpressionExtensions
{
    public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> leftExpression,
        Expression<Func<T, bool>> rightExpression) =>
        Combine(leftExpression, rightExpression, Expression.AndAlso);

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> leftExpression,
        Expression<Func<T, bool>> rightExpression) =>
        Combine(leftExpression, rightExpression, Expression.Or);

    public static Expression<Func<T, bool>> Combine<T>(Expression<Func<T, bool>> leftExpression, Expression<Func<T, bool>> rightExpression, Func<Expression, Expression, BinaryExpression> combineOperator)
    {
        var leftParameter = leftExpression.Parameters[0];
        var rightParameter = rightExpression.Parameters[0];

        var visitor = new ReplaceParameterVisitor(rightParameter, leftParameter);

        var leftBody = leftExpression.Body;
        var rightBody = visitor.Visit(rightExpression.Body);

        return Expression.Lambda<Func<T, bool>>(combineOperator(leftBody, rightBody), leftParameter);
    }

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

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

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return ReferenceEquals(node, _oldParameter) ? _newParameter : base.VisitParameter(node);
        }
    }
}

Usage is not changed and still like this:

Expression<Func<Result, bool>> noFilterExpression = item => filters == null;

Expression<Func<Result, bool>> laptopFilterExpression = item => item.x == ...
Expression<Func<Result, bool>> dateFilterExpression = item => item.y == ...

var combinedFilterExpression = noFilterExpression.Or(laptopFilterExpression.AndAlso(dateFilterExpression));
    
efQuery.Where(combinedFilterExpression);

(This is an example based on my actual code, but read it as pseudo-code)

Solution 6 - C#

I needed to achieve the same results, but using something more generic (as the type was not known). Thanks to marc's answer I finally figured out what I was trying to achieve:

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

Solution 7 - C#

I suggest one more improvement to PredicateBuilder and ExpressionVisitor solutions. I called it UnifyParametersByName and you can find it in MIT library of mine: LinqExprHelper. It allows for combining arbitary lambda expressions. Usually the questions are asked about predicate expression, but this idea extends to projection expressions as well.

The following code employs a method ExprAdres which creates a complicated parametrized expression, using inline lambda. This complicated expression is coded only once, and then reused, thanks to the LinqExprHelper mini-library.

public IQueryable<UbezpExt> UbezpFull
{
    get
    {
        System.Linq.Expressions.Expression<
            Func<UBEZPIECZONY, UBEZP_ADRES, UBEZP_ADRES, UbezpExt>> expr =
            (u, parAdrM, parAdrZ) => new UbezpExt
            {
                Ub = u,
                AdrM = parAdrM,
                AdrZ = parAdrZ,
            };

        // From here an expression builder ExprAdres is called.
        var expr2 = expr
            .ReplacePar("parAdrM", ExprAdres("M").Body)
            .ReplacePar("parAdrZ", ExprAdres("Z").Body);
        return UBEZPIECZONY.Select((Expression<Func<UBEZPIECZONY, UbezpExt>>)expr2);
    }
}

And this is the subexpression building code:

public static Expression<Func<UBEZPIECZONY, UBEZP_ADRES>> ExprAdres(string sTyp)
{
    return u => u.UBEZP_ADRES.Where(a => a.TYP_ADRESU == sTyp)
        .OrderByDescending(a => a.DATAOD).FirstOrDefault();
}

What I tried to achieve was to perform parametrized queries without need to copy-paste and with ability to use inline lambdas, which are so pretty. Without all these helper-expression stuff, I would be forced to create whole query in one go.

Solution 8 - C#

I think this works fine, isn't it ?

Func<T, bool> expr1 = (x => x.Att1 == "a");
Func<T, bool> expr2 = (x => x.Att2 == "b");
Func<T, bool> expr1ANDexpr2 = (x => expr1(x) && expr2(x));
Func<T, bool> expr1ORexpr2 = (x => expr1(x) || expr2(x));
Func<T, bool> NOTexpr1 = (x => !expr1(x));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBjartNView Question on Stackoverflow
Solution 1 - C#Marc GravellView Answer on Stackoverflow
Solution 2 - C#Adam TegenView Answer on Stackoverflow
Solution 3 - C#FrancisView Answer on Stackoverflow
Solution 4 - C#DejanView Answer on Stackoverflow
Solution 5 - C#Nick N.View Answer on Stackoverflow
Solution 6 - C#VorTechSView Answer on Stackoverflow
Solution 7 - C#JarekczekView Answer on Stackoverflow
Solution 8 - C#CélineView Answer on Stackoverflow