在EFcore中IEnumerable与IQueryable的区别

在EFcore中IEnumerable与IQueryable的区别

IQueryable 是 IEnumerable 的子类,所以 IQueryable 可以被转换为 IEnumerable,但是 IEnumerable 不可以被转换为 IQueryable。
IQueryable 是延迟执行的,也就是说,它不会立即执行,而是在调用 ToList()、ToArray() 等方法时才执行;而IEnumerable 是立即执行的。
同时Queryable中的where方法会返回一个IQueryable对象,而Enumerable中的where方法会返回一个IEnumerable对象。
但它们最主要的区别是一个是服务端评估,一个是客户端评估。


IEnumerable 是客户端评估,即在客户端执行,如果你要查询一条数据,而这个数据表里面一共有100万条数据,那么IEnumerable会把所有数据都拉下来,
然后在内存中过滤,CPU 和 GC 都会飙升,这样效率就很低了。
而 IQueryable 是服务端评估,即在使用SQL语句在数据库服务器上完成数据筛选,仅把最终结果返回给查询方。

IEnumerable 端

1
2
3
4
5
6
7
8
[HttpPost]
public async Task<ActionResult<string>> TestIEnumerableAsync_Select(string id)
{
IEnumerable<TestTable01> seq = _dateContext.TestTable01s;
var list = seq.Where(x => x.Id == id)
.ToList();//注意这里,需要ToList(),否则不会执行
return $"ok {list.Count}";
}

IQueryable 端

1
2
3
4
5
6
7
8
[HttpPost]
public async Task<ActionResult<string>> TestIQueryableAsync_Select(string id)
{
IQueryable<TestTable01> seq = _dateContext.TestTable01s;
var list = seq.Where(x => x.Id == id)
.ToListAsync();
return $"ok {list.Result.Count}";
}
目的 IQueryable 推荐 IEnumerable 备注
取第一条 FirstOrDefaultAsync(…) 无异步版本
统计 CountAsync(…) 全表拉回再 Count()
是否存在 AnyAsync(…) 全表拉回再 Any()

其实我觉得大多数人用的应该都是FirstOrDefaultAsync(…)这个方法吧,毕竟这个挺方便的。

控制台截图(如果图床崩了加载不出来可以看文字)

ajhsvb

IEnumerable控制台输出

1
2
3
4
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (21ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [t].[Id], [t].[Data]
FROM [TestTable01s] AS [t]

IQueryable控制台输出

1
2
3
4
5
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (33ms) [Parameters=[@__id_0='?' (Size = 50)], CommandType='Text', CommandTimeout='30']
SELECT [t].[Id], [t].[Data]
FROM [TestTable01s] AS [t]
WHERE [t].[Id] = @__id_0

仔细看两个控制台打印的sql语句,可以发现IQueryable的sql语句中多了where子句,并且where子句的参数是 @__id_0

方法 生成 SQL 参数 行数 评估位置
IEnumerable 分支 SELECT … FROM TestTable01s 无 WHERE 无参数 全表 客户端(内存过滤)
IQueryable 分支 SELECT … FROM TestTable01s WHERE Id = @__id_0 有参数 0~1 行 服务端(数据库过滤)

IEnumerable 端

Parameters=[] 并且 SQL 里完全没有 WHERE 子句 → EF 把整个表拉回内存,再由 Enumerable.Where 在内存里筛选。

IQueryable 端

带参数 @__id_0 且 SQL 出现 WHERE [t].[Id] = @__id_0 → 过滤逻辑被翻译成 SQL,只返回匹配行,网络 IO 最小。

数据表截图:
adsfsfdfes