Entity Framework Core在使用LINQ和谓词时不生成Where子句。
Entity Framework Core在使用LINQ和谓词时不生成Where子句。
错误标记的重复 - 请参见下面的答案
基本设置 - 我有一个应用程序上下文和一个用作DAO的抽象:
SomeEntity:
public class SomeEntity { public string MyProp { get; set; } }
DbContext:
public class ApplicationContext : DbContext { public DbSetSomeEntities { get; set; } /* 其他DbContext不重要。 */ }
DAO:
public class DAO { private readonly DbSet_dbSet; public DAO(ApplicationContext context) { _dbSet = context.SomeEntities; } public IEnumerable Where(Func predicate) { return _dbSet.Where(predicate); } }
用法:
Dao dao = new Dao(/* 实例化所需的任何参数 */); var results = dao.Where(e => e.MyProp == "my string");
期望的行为:
我希望EF Core生成像下面这样的SQL查询:
SELECT [e].MyProp FROM [TABLE_NAME] AS [e] WHERE [e].MyProp = 'my string'
实际行为:
EF Core生成以下SQL查询:
SELECT [e].MyProp FROM [TABLE_NAME] as [e]
它省略了where子句,导致应用程序在筛选之前将所有记录都加载到内存中。
为什么?
问题的原因在于,您将一个Func<SomeEntity, bool>
传递给LINQ的Where
方法作为谓词。当您将Func
传递给Where
时,它返回一个IEnumerable
。
当您告诉EF Core返回一个IEnumerable
时,您是在告诉它您已经完成了对数据集的查询,并希望枚举结果。因此,它生成一个没有正确的服务器端WHERE
子句的查询,并一次性提取所有数据。
在DAO类中,不要接受Func
作为参数,而是接受Expression<Func<SomeEntity, bool>>
,因为当您将Expression
传递给LINQ的Where
方法时,它将返回一个IQueryable
而不是一个IEnumerable
。这样做有两个作用:
- 允许您在执行之前添加其他操作到查询中。
- 告诉EF Core在将结果集返回给应用程序之前要在SQL中过滤记录。
所以,代替:
public IEnumerable<SomeEntity> Where(Func<SomeEntity, bool> predicate) { return _dbSet.Where(predicate); }
应该是:
public IEnumerable<SomeEntity> Where(Expression<Func<SomeEntity, bool>> predicate) { return _dbSet.Where(predicate); }
用法:
Dao dao = new Dao(/* whatever for instantiation */); var results = dao.Where(e => e.MyProp == "my string");
生成的查询:
SELECT [e].MyProp FROM [TABLE_NAME] as [e] WHERE [e].MyProp = 'my string'
请注意,方法的实现实际上没有改变,使用方式也没有改变- 这是因为某些形式的Func
和Expression
是可以互换的,在这种情况下使用其中一个或另一个不会导致编译错误,因此在本质上是相同的。
我通过查看Queryble.Where
(和其他Queryable
)扩展方法的签名找到了这个答案。 Expression<Func<...>>
与Func<...>
之间的基本区别是Queryable
和Enumerable
LINQ查询之间的区别。
对于那些不熟悉.NET的人来说,返回一个IQueryable与返回一个IEnumerable的事实并不足以让人们将其与不构建SQL查询联系起来。感谢您的参与。