我写了一些参数化 Lambda 查询
//Method 1:
Func<SalesOrderLineEntity, bool> func01 = (o => o.SOLNumber == "123456");
var q01 = _context.SalesOrderLineEntities.Where(func01).ToList();
//Got the result, but SQLServer Read All Records to memory before "where"
//Method 2:
Expression<Func<SalesOrderLineEntity, bool>> exp02 = (o => o.SOLNumber == "123456");
var q02 = _context.SalesOrderLineEntities.Where(exp02).ToList();
//Got the result,Exec "Where" in SQLServer
//Method 3:
Expression<Func<SalesOrderLineEntity, bool>> exp03 = (o => func01(o));
var q03 = _context.SalesOrderLineEntities.Where(exp03.Compile()).ToList();
//Same to Method 1,because Compile() result is Func<SalesOrderLineEntity, bool>
//Method 4:
var q04 = _context.SalesOrderLineEntities.Where(exp03).ToList();
//Error:The LINQ expression node type 'Invoke' is not supported in LINQ to Entities
方法1和3:效率很低方法4:错误
方法2:需要我通过lambda构建一个表达式。我觉得这很难,因为我会用到很多"if,else",更容易创建一个函数。正确的方法是什么?
变体
方法 1:EF 从数据库读取所有记录,因为您将Func
传递到 Where
子句中,这不是正确的候选项:EF 无法从中提取生成查询所需的信息,它只能在内存中集合上使用该函数。
2:这是执行 EF 查询的正确方法,因为 EF 基于Expression tree
构建实际查询。当您编写.Where
时,它可能看起来像方法 1 相同,但这是不同的。
IQueryable 扩展方法使用表达式树,因此您可以(或 EF 可以)在运行时计算该信息。
方法3:这本质上与方法 1 相同,因为您编译了表达式。这是使用它们时的一个关键区别:表达式包含构建实际操作的信息,但这不是操作本身。你需要在之前编译它(或者例如,你可以基于它们生成 SQL 查询,这就是 EF 的工作方式)。
方法 4:EF 无法将func01()
调用转换为任何 SQL 函数。它无法翻译任何类型的代码,因为它需要等效的 SQL 操作。你可以尝试使用通用方法,你会得到相同的结果,这与 Func 无关。
这里发生了什么?
如果我们简化底层流程,那么上面的答案可能会更清晰。
//Method 2:
Expression<Func<SalesOrderLineEntity, bool>> exp02 = (o => o.SOLNumber == "123456");
var q02 = _context.SalesOrderLineEntities.Where(exp02).ToList();
//Got the result,Exec "Where" in SQLServer
EF 可以读取以下内容(通过表达式):
- 用户希望使用
Where
进行筛选 - 这是一个表达式,让我们获取一些信息
- 好吧,它需要
SalesOrderLineEntity
,我有该类型的映射 - 表达式告知属性
SOLNumber
必须等于"123456" - 好的,我有一个
SOLNumber
的映射,所以很好 - 我可以将相等运算符转换为等效的 SQL 运算符
- 一切正常,因此我们可以构建SQL查询
当然,您不能使用Func
来执行此操作,例如,因为该对象不包含这些信息。
不确定这是否适用,但您是否查看了编译的查询: 编译查询(LINQ to Entities),这应该会产生更高效的 SQL 语句