实体框架过滤器“Expression <func <t,bool =”“>&gt;”</func <t,>

时间:2013-08-20 14:30:54

标签: c# entity-framework lambda

我正在尝试为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中更改它以使用相关表。 我宁愿远离其他外部图书馆,因为我想要的是学习它是如何工作的,并且能够在将来的任何场景中使用它。

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

3 个答案:

答案 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