【问题标题】:The LINQ expression could not be translated (Generic class)无法翻译 LINQ 表达式(通用类)
【发布时间】:2020-11-01 22:36:33
【问题描述】:

我在 SO 上看到了十几个这样的问题,但没有一个对我有任何帮助,所以我决定再发布一个,尝试解决我的问题。

我有一个GenericRepository<T> 类作为基本CRUD 方法的模板,因为我有一个比较大的模型包,所以我不想一遍又一遍地为不同的类实现相同的方法(不要重复自己)。

当我尝试使用Predicate<T> 创建用于查询的自定义方法时,出现了问题。 这是我的代码:

        public IEnumerable<T> GetMatching(Predicate<T> condition)
             => dbContext.Set<T>.Where(entity => condition(entity));

在我看来,这样做的目的是能够传递任何类型的查询,对其进行评估并作为结果返回。

这是我尝试通过使用名为 EmployeeID 的字段过滤它们来获取一些 Doctor 对象的部分:

Repository.GetMatching(doctor => doctor.EmployeeID > 10);

这个想法是,给我一个医生名单,他们的EmployeeID 数字字段大于 10。 不幸的是,这给了我以下错误:

Unhandled exception. System.InvalidOperationException: The LINQ expression 'DbSet<Doctor>
.Where(d => Invoke(__condition_0, d[Doctor]))' 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 either 
AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().

根据错误信息,我尝试在GetMatching()函数的末尾添加ToList(),但没有成功,仍然失败并显示相同的错误信息。

我错过了什么?

【问题讨论】:

    标签: entity-framework entity-framework-core linq-to-entities


    【解决方案1】:

    Expression&lt;Func&lt;T, bool&gt;&gt;,而不是Predicate,然后直接传入:

        public IEnumerable<T> GetMatching(Expression<Func<T, bool>> condition)
             => dbContext.Set<T>.Where(condition);
    

    这样做的原因是实体框架依赖于表达式树来确定如何将 LINQ 代码转换为 SQL 查询。当它正在评估的表达式(您传递给 Where 的 lambda)调用 Entity Framework 不知道如何翻译的函数(例如某人传递给您的函数的任意委托)时,它会窒息。

    附带说明,请注意以下事实:返回 IEnumerable&lt;T&gt; 将使添加到返回值的任何其他 LINQ 代码都被视为 LINQ-to-Objects,而不是由实体框架评估。如果这是您想要的,请考虑调用.ToList() 并返回IReadOnlyCollection&lt;&gt; 之类的内容,以避免不必要地延迟执行。如果没有,请考虑返回IQueryable&lt;&gt;

    【讨论】:

    • 非常感谢您的精彩回答,您节省了我的时间 :)
    • 如果我可能会问,您所说的“避免不必要地延迟执行”到底是什么意思?返回 IEnumerable&lt;T&gt; 会减慢我的程序吗?
    • @ForWiz:如果返回的IEnumerable&lt;&gt; 直到 dbContext 被释放后才被评估,你会得到一个异常。如果在您对数据库进行更改之后才对其进行评估,则这些更改将反映在返回的结果中(请参阅this example)。如果使用此方法的代码假定匹配项将在方法返回之前从数据库中加载,您应该强制这样做以避免意外行为。
    • 在性能方面,如果将.Where().GroupBy() 添加到GetMatching() 的结果中,这些操作在数据库层可能会更加高效。因此,将您的 IQueryable&lt;&gt; 强制转换为 IEnumerable&lt;&gt; 可能会减慢您的程序。但是由于执行延迟,如果您测量调用 GetMatching() 的性能,它会显得非常快,因为它实际上什么也没做。但是后来一些迭代结果的代码会运行得很慢,因为这是数据库调用(和后续 LINQ 操作)将被调用的地方。
    猜你喜欢
    • 2021-12-28
    • 1970-01-01
    • 2022-12-03
    • 1970-01-01
    • 2021-11-27
    • 2022-10-20
    • 2020-09-29
    • 2021-07-01
    相关资源
    最近更新 更多