无法翻译 LINQ 表达式 'DbSet<Person>() .Where(p => p.Id % 2 == 0 ? Even : Odd == Even)'

The LINQ expression 'DbSet<Person>() .Where(p => p.Id % 2 == 0 ? Even : Odd == Even)' could not be translated

我有一个 class 人,它有一些聚合值,例如如果 ID 是偶数或奇数。

public class Person
{
    public int Id { get; set; }
    // ... some properties
    public static Expression<Func<Person, NumberType>> NumberType
    {
        get
        {
            return p => ((p.Id % 2 == 0)
                ? Shared.NumberType.Even
                : Shared.NumberType.Odd);
        }
    }
}

public enum NumberType
{
    Even = 0,
    Odd = 1
}

现在如果我尝试让我的人拥有偶数 ID

// this is DbSet<Person>
var people = dbContext.People.AsQueryable();

// this alone works
people = people.Where(p => ((p.Id % 2 == 0) ? Shared.NumberType.Even : Shared.NumberType.Odd) == Shared.NumberType.Even);

// this should do exactly the same as above but results in the following error as soon as the queryable is evaluated
var body = Expression.MakeBinary(ExpressionType.Equal, Person.LocalNumberType.Body, Expression.Constant(Shared.NumberType.Even));
people =  people.Where(Expression.Lambda<Func<Person, bool>>(body, Expression.Parameter(typeof(Person), "p")));


// exception thrown here
var evenCount = people.Count();

处理请求时发生未处理的异常。 InvalidOperationException:LINQ 表达式 'DbSet() .Where(p => (int)p.Id % 2 == 0 ? Even : Odd == 0) .Where(p => p.Id % 2 == 0 ? Even : Odd == Even)' 无法翻译。要么以可以翻译的形式重写查询,要么通过插入对 'AsEnumerable'、'AsAsyncEnumerable'、'ToList' 或 'ToListAsync' 的调用显式切换到客户端评估。有关详细信息,请参阅 https://go.microsoft.com/fwlink/?linkid=2101038

// FUNFACT, ordering by the Expression works
var orderByExp = (typeof(Person).GetProperty("NumberType")!.GetValue(null, null) as LambdaExpression);
people = people.Provider.CreateQuery<Person>(Expression.Call(
    typeof(Queryable),
    "orderBy",
    new Type[] {
        typeof(Person),
        orderByExp.ReturnType
    },
    people.Expression,
    Expression.Quote(orderByExp)));

lambda 表达式中的参数表达式是按实例标识的,而不是像你想象的那样按名称标识。所以这里

var body = Expression.MakeBinary(ExpressionType.Equal, Person.LocalNumberType.Body, Expression.Constant(Shared.NumberType.Even));
people =  people.Where(Expression.Lambda<Func<Person, bool>>(body, Expression.Parameter(typeof(Person), "p")));

正文绑定到源 Person.NumberType 表达式参数,但是您在新的 lambda 表达式中使用它,参数 不同(事件虽然相同类型和名称)。

一种可能的解决方案是重用原始参数,例如

var source = Person.LocalNumberType;
var body = Expression.Equal(source.Body, Expression.Constant(Shared.NumberType.Even));
var predicate = Expression.Lambda<Func<Person, bool>>(body, source.Parameters);

people =  people.Where(predicate);