实体框架过滤器“表达式<Func<T, bool>>”

Posted

技术标签:

【中文标题】实体框架过滤器“表达式<Func<T, bool>>”【英文标题】:Entity Framework Filter "Expression<Func<T, bool>>" 【发布时间】:2013-08-22 15:50:37 【问题描述】:

我正在尝试为实体框架列表创建过滤方法并更好地理解

Expression&lt;Func&lt;...

我有一个这样的测试函数。

public IQueryable<T> Filter<T>(IEnumerable<T> src, Expression<Func<T, bool>> pred)

    return src.AsQueryable().Where(pred);

如果我这样做:

context.Table.Filter(e => e.ID < 500);

或者这个:

context.Table.Filter(e => e.SubTable.Where(et => et.ID < 500).Count() > 0 && e.ID < 500);

一切正常。

但如果我这样做:

context.Table.Filter(e => e.SubTable.Filter(et => et.ID < 500).Count() > 0 && e.ID < 500);

或者这个:

context.Table.Where(e => e.SubTable.Filter(et => et.ID < 500).Count() > 0 && e.ID < 500);

我收到一个错误。 LINQ to Entities does not recognise the method ...Filter...

为什么它在一种情况下有效,而在另一种情况下无效?我应该在过滤器中进行哪些更改以使其与相关表一起使用? 我更喜欢远离其他外部库,因为我想要了解它是如何工作的,并且能够在未来的任何场景中使用它。

在前两种情况下,过滤器在数据库中正确运行。

【问题讨论】:

【参考方案1】:

Jon 和 Tim 已经解释了为什么它不起作用。

假设Filter 中的过滤器代码不是微不足道的,您可以更改Filter 使其返回一个EF 可以翻译的表达式。

假设你有这个代码:

context.Table.Where(x => x.Name.Length > 500);

您现在可以创建一个返回此表达式的方法:

Expression<Func<YourEntity, bool>> FilterByNameLength(int length)

    return x => x.Name.Length > length;

用法如下:

context.Table.Where(FilterByNameLength(500));

您在FilterByNameLength 中构建的表达式可以任意复杂,只要您可以将其直接传递给Where

【讨论】:

通过大量阅读和反复试验,我怀疑这是唯一的方法。它是否适用于 context.Table.Where(e => e.subTable.Any(MyFilter())); @Pedro.The.Kid:不,不会。这个方法的结果需要传递给最外层的Where,因为你传递给最外层的Where的每个表达式实际上并没有被执行而是被解释。 我做了一个测试,这很有效 var filter = MyFilter(); context.Table.Where(e => e.subTable.AsQueryable().Any(filter)); @Pedro:很有趣。 EF 似乎在评估它,因为从表达式的 POV 来看,它是一个常数。很好的发现。【参考方案2】:

了解Expression&lt;Func&lt;&gt;&gt;Func&lt;&gt; 之间的区别很有用。

Expression e =&gt; e.ID &lt; 500 存储有关该表达式的信息:有一个 T e,您正在访问属性 ID,使用 &lt; 值调用 &lt; 运算符500。当 EF 看到它时,它可能会将其变成类似 [SomeTable].[ID] &lt; 500 的东西。

Func e =&gt; e.ID &lt; 500 是等价于:

static bool MyMethod(T e)  return e.ID < 500; 

它被编译为执行此操作的 IL 代码;它不是为了“重构”为 SQL 查询或其他任何东西而设计的,只能运行。

当 EF 获取您的 Expression 时,它必须了解其中的每一部分,因为它使用它来构建 SQL 查询。它被编程为知道现有的Where 方法的含义。它不知道你的Filter 方法是什么意思,即使它是一个微不足道的方法,所以它就放弃了。

【讨论】:

【参考方案3】:

为什么它在一种情况下有效,而不是在加法器中?

因为 EF 并不真正“了解”您的 Filter 方法。它不了解它的含义,因此不知道如何将其转换为 SQL。与它确实理解的Where 等进行比较。

在初始表上直接调用它的版本可以工作,因为这样你就不会得到一个包含对Filter的调用的表达式树——它只是直接调用Filter ,这反过来确实构建了一个查询...但是 EF 可以理解。

如果您能想出一种方法让您的 Filter 方法在 EF 查询中工作,我会感到非常惊讶...但是您已经说过使用 Where 无论如何都可以,那么为什么要使用 @ 987654327@ 呢?我会使用 Where 版本 - 或者更好的是,使用带有谓词的 Any 重载:

context.Table.Filter(e => e.SubTable.Any(et => et.ID < 500) && e.ID < 500);

【讨论】:

有什么办法可以改变它以得到一个不包含方法名称的表达式树? @Pedro.The.Kid:不容易。您可以编写一个方法来重写表达式树并在此基础上创建一个新查询,但不清楚您为什么要这样做,而且会很棘手。 至于使用Where只是为了方便在真实环境中测试,会复杂很多。 @Pedro.The.Kid:嗯,不知道你真正试图实现什么,很难帮助你 - 但基本上你会很难尝试做这样的事情。 我知道这是一个旧线程,但挖掘 EF 让我认为这种方法行不通,因为它将表转换为 IEnumerable,然后使用 AsQueryable,这将使查询该 IEnumerable 而不是创建所需的 SQL 查询。如果过滤方法 src 参数是 IQueryable 会不会起作用?

以上是关于实体框架过滤器“表达式<Func<T, bool>>”的主要内容,如果未能解决你的问题,请参考以下文章

实体框架过滤器索引

实体框架4:使用自我跟踪实体的过滤器进行预先加载(包括)

使用实体框架过滤BindingSource

实体框架6过滤子对象

您如何在实体框架的数据库级别执行复杂的“或”过滤器?

如何过滤嵌套集合实体框架对象?