实体框架过滤器“表达式<Func<T, bool>>”
Posted
技术标签:
【中文标题】实体框架过滤器“表达式<Func<T, bool>>”【英文标题】:Entity Framework Filter "Expression<Func<T, bool>>" 【发布时间】:2013-08-22 15:50:37 【问题描述】:我正在尝试为实体框架列表创建过滤方法并更好地理解
Expression<Func<...
我有一个这样的测试函数。
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<Func<>>
和Func<>
之间的区别很有用。
Expression
e => e.ID < 500
存储有关该表达式的信息:有一个 T
e
,您正在访问属性 ID
,使用 <
值调用 <
运算符500
。当 EF 看到它时,它可能会将其变成类似 [SomeTable].[ID] < 500
的东西。
Func
e => e.ID < 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>>”的主要内容,如果未能解决你的问题,请参考以下文章