为什么EntityFramework';的LINQ解析器以不同的方式处理外部定义的谓词
本文关键字:方式 处理 谓词 定义 外部 EntityFramework LINQ 为什么 | 更新日期: 2023-09-27 18:21:12
我正在使用Microsoft的实体框架作为ORM,并想知道如何解决以下问题。我想从Products
集合中获得一些Product
对象,其中Product.StartDate
比今天大。(这是整个问题的简化版本。)
我目前使用:
var query = dbContext.Products.Where(p => p.StartDate > DateTime.Now);
当执行此操作时,例如在查询中使用ToList()
之后,它就工作了,并且创建的SQL是有效的:
SELECT * FROM Product WHERE StartDate > (GetDate());
然而,为了更好地维护,我想把谓词移到一个函数上,所以我尝试了这个:
private Func<Product, bool> GetFilter()
{
Func<Product, bool> filter = p => p.StartDate > DateTime.Now;
return filter;
}
var query = dbContext.Products.Where(GetFilter());
从代码的角度来看,这也是有效的,因为它返回相同的Product
集,但这次创建的SQL类似于:
SELECT * FROM Product;
过滤器从SQL Server移动到客户端,使其效率大大降低。
所以我的问题是:
- 为什么会发生这种情况,为什么LINQ解析器对待这两种格式如此不同
- 我可以做些什么来利用过滤器是单独的,但在服务器上执行它
您需要使用Expression<Func<Product, bool>>
才能使其按您的意愿工作。一个普通的Func<Product, bool>
命令告诉LINQ,您希望它在程序的MSIL中运行Where
,而不是在SQL中。这就是为什么SQL拉入整个表,然后您的.NET代码在整个表上运行谓词。
您返回的是Func
,但要将谓词注入SQL,LINQ需要一个表达式树。如果您将方法(当然还有局部变量)的返回类型更改为Expression<Func<Product, bool>>
,它应该会起作用。
因为在第二种情况下,filter func可能是任意的LINQ to EF,无法将您的筛选器解析为SQL,因此必须在客户端进行解析。