Search code examples
c#entity-framework-coreexpression-trees

C# Expression Tree AndAlso for Entity Framework query


I get an error when trying to combine two expressions with AndAlso.

Step 1: building expression for EF object with expression tree:

public override Expression<Func<T, bool>> ToExpression()
{
    var expressionParameter = Expression.Parameter(typeof(T), "p");
    var expressionField = Expression.PropertyOrField(expressionParameter, field);
    var expressionConstraint = Expression.Constant(value);

    BinaryExpression expression = Expression.Equal(expressionField, expressionConstraint);

    return Expression.Lambda<Func<T, bool>>(expression, expressionParameter);
}

If I'm calling .ToExpression(); and running this for one expression, this code works fine with EF, it produces expression:

{ p => (p.MyField1 == "X") }

But when I'm trying to do

Step 2: combine two expressions with AndAlso:

public override Expression<Func<T, bool>> ToExpression()
{
    Expression<Func<T, bool>> leftExpression = left.ToExpression();
    Expression<Func<T, bool>> rightExpression = right.ToExpression();

    BinaryExpression andExpression = Expression.AndAlso(leftExpression.Body, rightExpression.Body);

    return Expression.Lambda<Func<T, bool>>(andExpression, leftExpression.Parameters.Single());
}

This produces an expression:

{ p => ((p.MyField1 == "X") AndAlso (p.MyField2 == "Y")) }

It seems fine to me, but when trying to call it with .Where(expression) I get this error:

System.InvalidOperationException: The LINQ expression 'p' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'

Any idea why separate expressions work but combined with AndAlso don't?


Solution

  • Implementing ParameterReplacer did the trick, implementation that I have found and used:

        internal 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) => node == _oldValue ? _newValue : base.Visit(node);
    }
    

    Thank's for your suggestions.