23

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

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...

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

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

4

3 回答 3

36

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

于 2013-08-20T15:01:54.490 回答
14

Expression<Func<>>了解和之间的区别很有用Func<>

AnExpression e => e.ID < 500存储有关该表达式的信息:存在 a T e,您正在访问 property ,并使用valueID调用<运算符。当 EF 看到它时,它可能会将其变成类似.int500[SomeTable].[ID] < 500

AFunc e => e.ID < 500是一种等效于的方法:

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

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

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

于 2013-08-20T14:45:04.443 回答
9

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

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

您直接在初始表上调用它的版本是有效的,因为这样您就不会得到一个包含调用的表达式树Filter- 它只是Filter直接调用,这反过来建立一个查询......但是一个 EF明白。

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

context.Table.Filter(e => e.SubTable.Any(et => et.ID < 500) && e.ID < 500);
于 2013-08-20T14:37:48.213 回答