为什么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解析器对待这两种格式如此不同
  • 我可以做些什么来利用过滤器是单独的,但在服务器上执行它

为什么EntityFramework';的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,因此必须在客户端进行解析。