C# 表达式树 AndAlso 用于 Entity Framework 查询

huangapple go评论52阅读模式
英文:

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&lt;Func&lt;T, bool&gt;&gt; ToExpression()
{
    var expressionParameter = Expression.Parameter(typeof(T), &quot;p&quot;);
    var expressionField = Expression.PropertyOrField(expressionParameter, field);
    var expressionConstraint = Expression.Constant(value);

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

    return Expression.Lambda&lt;Func&lt;T, bool&gt;&gt;(expression, expressionParameter);
}

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

{ p =&gt; (p.MyField1 == &quot;X&quot;) }

But when I'm trying to do

Step 2: combine two expressions with AndAlso:

public override Expression&lt;Func&lt;T, bool&gt;&gt; ToExpression()
{
    Expression&lt;Func&lt;T, bool&gt;&gt; leftExpression = left.ToExpression();
    Expression&lt;Func&lt;T, bool&gt;&gt; rightExpression = right.ToExpression();

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

    return Expression.Lambda&lt;Func&lt;T, bool&gt;&gt;(andExpression, leftExpression.Parameters.Single());
}

This produces an expression:

{ p =&gt; ((p.MyField1 == &quot;X&quot;) AndAlso (p.MyField2 == &quot;Y&quot;)) }

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&lt;Func&lt;T, bool&gt;&gt;(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) =&gt; node == _oldValue ? _newValue : base.Visit(node);
}

Thank's for your suggestions.

huangapple
  • 本文由 发表于 2023年7月17日 21:44:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76705074.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定