我正在尝试为Entity framework List创建一个过滤方法,并更好地理解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 recognize the method ...Filter...
为什么它适用于一个案例而不是加法器?我应该在Filter中更改它以使用相关表。 我宁愿远离其他外部图书馆,因为我想要的是学习它是如何工作的,并且能够在将来的任何场景中使用它。
在前两种情况下,过滤器在数据库中正确运行。
答案 0 :(得分:26)
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
。
答案 1 :(得分:9)
理解Expression<Func<>>
和Func<>
之间的区别非常有用。
Expression
e => e.ID < 500
存储有关该表达式的信息:有T
e
,您正在访问该属性ID
,并调用<
值为int
500
的{{1}}运算符。当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
方法意味着什么,即使它是一个微不足道的方法,所以它只是放弃了。
答案 2 :(得分:6)
为什么它适用于一个案例而不是加法器?
因为EF并不真正“了解”您的Filter
方法。它不了解它的意图,因此它不知道如何将其转换为SQL。将其与 理解的Where
等进行比较。
在初始表上直接调用 的版本可以正常工作,因为这样你就不会得到一个包含对Filter
的调用的表达式树 - 它只是调用{{ 1}}直接,反过来 建立一个查询...但是EF理解的那个。
如果您能够找到一种方法让您的Filter
方法在EF查询中工作,我会感到非常惊讶......但您已经说过使用Filter
无论如何都可行,那么为什么要使用Where
呢?我使用Filter
版本 - 或者更好的是,使用带有谓词的Where
重载:
Any