英文:
C# Expression Tree AndAlso for Entity Framework query
问题
在尝试使用AndAlso组合两个表达式时,我遇到了错误。
步骤1:使用表达式树构建EF对象的表达式:
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);
}
如果我调用.ToExpression()并针对一个表达式运行此代码,这段代码在EF中可以正常工作,它生成了表达式:
{ p => (p.MyField1 == "X") }
但是,当我尝试进行以下操作时:
步骤2:使用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());
}
这会生成一个表达式:
{ p => ((p.MyField1 == "X") AndAlso (p.MyField2 == "Y")) }
在尝试使用.Where(expression)调用它时,我得到了以下错误:
System.InvalidOperationException: LINQ表达式'p'无法被转换。要么以可翻译的形式重写查询,要么通过插入对'AsEnumerable'、'AsAsyncEnumerable'、'ToList'或'ToToListAsync'的调用显式切换到客户端评估。
为什么单独的表达式可以工作,但与AndAlso组合的表达式不能工作呢?
英文:
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?
答案1
得分: 1
你的 Expression.Lambda 调用可能需要从一个像这样的 Expression.Invoke 调用开始:
var expr = Expression.Invoke(expression, expressionParameter);
return Expression.Lambda<Func<T, bool>>(expr, expressionParameter);
英文:
Your Expression.Lambda calls probably need to start with a call to Expression.Invoke like this:
var expr = Expression.Invoke(expression, expressionParameter);
return Expression.Lambda<Func<T, bool>>(expr, expressionParameter);
答案2
得分: 0
实现ParameterReplacer起到了作用,我找到并使用的实现如下:
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);
}
感谢您的建议。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论