实体框架过滤器"Expression <Func <T,bool >>"

Ped*_*Kid 16 c# lambda entity-framework

我正在尝试为Entity框架列表创建一个过滤器方法并更好地理解 Expression<Func<...

我有这样的测试功能.

public IQueryable<T> Filter<T>(IEnumerable<T> src, Expression<Func<T, bool>> pred)
{
    return src.AsQueryable().Where(pred);
}
Run Code Online (Sandbox Code Playgroud)

如果我这样做:

context.Table.Filter(e => e.ID < 500);
Run Code Online (Sandbox Code Playgroud)

或这个:

context.Table.Filter(e => e.SubTable.Where(et => et.ID < 500).Count() > 0 && e.ID < 500);
Run Code Online (Sandbox Code Playgroud)

一切都运作良好.

但如果我这样做:

context.Table.Filter(e => e.SubTable.Filter(et => et.ID < 500).Count() > 0 && e.ID < 500);
Run Code Online (Sandbox Code Playgroud)

或这个:

context.Table.Where(e => e.SubTable.Filter(et => et.ID < 500).Count() > 0 && e.ID < 500);
Run Code Online (Sandbox Code Playgroud)

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

为什么它适用于一个案例而不是加法器?我应该在Filter中更改它以使用相关表.我更喜欢远离其他外部库,因为我想要的是学习它是如何工作的,并且能够在将来的任何场景中使用它.

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

Dan*_*rth 26

乔恩和蒂姆已经解释了为什么它不起作用.

假设里面的过滤器代码Filter不是微不足道的,你可以改变Filter它以便它返回一个EF可以翻译的表达式.

我们假设你有这个代码:

context.Table.Where(x => x.Name.Length > 500);
Run Code Online (Sandbox Code Playgroud)

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

Expression<Func<YourEntity, bool>> FilterByNameLength(int length)
{
    return x => x.Name.Length > length;
}
Run Code Online (Sandbox Code Playgroud)

用法如下:

context.Table.Where(FilterByNameLength(500));
Run Code Online (Sandbox Code Playgroud)

你在里面建立的表达式FilterByNameLength可以是任意复杂的,只要你可以直接传递给它Where.


Tim*_* S. 10

这是非常有用的理解之间的差异Expression<Func<>>Func<>.

一个Expression e => e.ID < 500存储有关表达的信息:这有一个T e,你所访问的属性ID,调用<与运营商int价值500.当EF看到它时,它可能会变成类似的东西[SomeTable].[ID] < 500.

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

static bool MyMethod(T e) { return e.ID < 500; }
Run Code Online (Sandbox Code Playgroud)

它被编译为执行此操作的IL代码; 它不是设计成'重构'到SQL查询或其他任何东西,只运行.

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


Jon*_*eet 6

为什么它适用于一个案例而不是加法器?

因为EF并不真正"了解"你的Filter方法.它不了解它的意图,因此它不知道如何将其转换为SQL.与之相比Where,它确实理解.

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

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

context.Table.Filter(e => e.SubTable.Any(et => et.ID < 500) && e.ID < 500);
Run Code Online (Sandbox Code Playgroud)