Simon Descamps
Simon Descamps

Reputation: 53

Combining two expressions (Expression<Func<T, bool>>) in a single one

I want to combile two expressions :

public Expression<Func<AnyType,bool>> BuildExpression(int id, string filtreA, string filtreB)
    {
        Expression<Func<AnyType, bool>> extraExpression = null;
        Expression<Func<AnyType, bool>> expression = null;

        expression = a => a.ID == id;

        var fullExpression = expression.Body;
      
        if (!String.IsNullOrEmpty(filtreA))
        {
            extraExpression = a => a.TYPE == filtreA;
            fullExpression = Expression.AndAlso(fullExpression, extraExpression.Body);
        }
         if (!String.IsNullOrEmpty(filtreB))
        {
            extraExpression = a => a.TYPE == filtreB;
            fullExpression = Expression.AndAlso(fullExpression, extraExpression.Body);
        }
    
        expression = Expression.Lambda<Func<AnyType, bool>>(fullExpression, expression.Parameters[0]);
        }

        return expression;
    }

    //I want my final expression to be a.ID == id && a.TYPE == filtreB for example

The problem is that I get the following error : System.InvalidOperationException. Le paramètre 'a' n'est pas dans la portée.. It only happens if enter in one of my ifs.

Does anyone know how I can handle this ? Thanks

ps: My question il similar to this post but it looks like the solution doesn't work anymore : Combining two expressions (Expression<Func<T, bool>>)

Upvotes: 0

Views: 2698

Answers (2)

Jeremy Lakeman
Jeremy Lakeman

Reputation: 11100

Each parameter is a different ParameterExpression instance. This problem would be more obvious if you had defined each expression with different parameter names;

expression = a => a.ID == id;
// ...
extraExpression = b => b.TYPE == filtreA;
// ...
extraExpression = c => c.TYPE == filtreB;

Note that entity framework core 2.1 and earlier didn't care if your parameters were different, and would seem to look through .Invoke operations. But since version 3, with an internal rewrite of the expression compiler, neither of those are supported. Hence why earlier examples may not work anymore.

An expression visitor to swap parameter expressions isn't too complicated;

public class MapParameters : ExpressionVisitor
{
    private readonly Dictionary<ParameterExpression, ParameterExpression> mapping;
    public MapParameters(IEnumerable<ParameterExpression> before, IEnumerable<ParameterExpression> after)
    {
        this.mapping = new Dictionary<ParameterExpression, ParameterExpression>(
            before
                .Zip(after)
                .Select(p => KeyValuePair.Create(p.First,p.Second))
        );
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (mapping.TryGetValue(node, out var replace))
            return replace;
        return base.VisitParameter(node);
    }
}

Expression.Lambda<Func<AnyType, bool>>(
    Expression.AndAlso(
        expression.Body,
        new MapParameters(
            extraExpression.Parameters,
            expression.Parameters
        ).Visit(extraExpression.Body)
    ),
    expression.Parameters);

Also note that if you are using this to build a single expression for IQueryable<T>.Where(...). You could consider calling .Where(...) twice, which would achieve the same end result.

Upvotes: 1

Svyatoslav Danyliv
Svyatoslav Danyliv

Reputation: 27282

You have missed parameters replacement. It is a tricky with visitors. Trying to simplify your life, I would suggest to use popular library LINQKit and rewrite your expression building.

public Expression<Func<AnyType, bool>> BuildExpression(int id, string filtreA, string filtreB)
{
    var predicate = PredicateBuilder.New<AnyType>(true);

    predicate = predicate.And(a => a.ID == id);

    if (!string.IsNullOrEmpty(filtreA))
    {
        predicate = predicate.And(a => a.TYPE == filtreA);
    }

    if (!string.IsNullOrEmpty(filtreB))
    {
        predicate = predicate.And(a => a.TYPE == filtreB);
    }
    
    return predicate;
}

Upvotes: 1

Related Questions