为何对Queryable调用.Count()后,.ToList()返回0条数据?
问题描述
我现在有一段EF查询代码:
query = query.DistinctBy(q => q.Series.Name) .OrderBy(c => c.Series.RoleId.Value == (int)Type.Type1 ? 0 : 1) .ThenBy(c => c.Series.RoleId.Value == (int)Type.Type2 ? 0 : 1) .ThenBy(s => s.Series.RoleId) .AsQueryable(); } query = query .OrderByDescending(a => a.Series.Date) .ThenByDescending(a => a.Series.Id); var total = query.Count(); var states = query .Skip((model.pageNumber - 1) * model.pageSize) .Take(model.pageSize); var return = Mapper.Map<List<SeriesDTO>>(states);
我的核心需求是获取执行Skip和Take分页操作前的结果集总数,但遇到了奇怪的问题:
- 直接调用
query.Count()返回0,但此时后续的Mapper.Map能正常返回数据 - 换成
query.ToList().Count()能得到正确总数,但此时Mapper.Map返回空数据 - 我也试过把
states直接ToList(),问题依旧
解决方案
问题根源
DistinctBy是LINQ to Objects的方法,并非EF原生支持的查询方法。调用DistinctBy后再转AsQueryable(),实际查询已经在内存中执行了一部分,但后续的OrderByDescending等操作又试图转为数据库查询,导致Count()执行时生成的SQL存在问题,返回0;而ToList()会把所有数据加载到内存,所以能得到正确总数,但此时原query的枚举器已经被耗尽,后续的Skip/Take自然拿不到数据。
修复步骤
- 替换
DistinctBy为EF支持的去重方式
如果使用EF Core,可以用GroupBy实现按Series.Name去重,确保整个查询能被EF正确转为SQL执行:query = query.GroupBy(q => q.Series.Name) .Select(g => g.First()) .OrderBy(c => c.Series.RoleId.Value == (int)Type.Type1 ? 0 : 1) .ThenBy(c => c.Series.RoleId.Value == (int)Type.Type2 ? 0 : 1) .ThenBy(s => s.Series.RoleId) .OrderByDescending(a => a.Series.Date) .ThenByDescending(a => a.Series.Id); - 正确获取总数和分页数据
确保Count()和分页操作基于同一个可被EF正确解析的查询对象:// 先获取总数 var total = query.Count(); // 再执行分页 var states = query.Skip((model.pageNumber - 1) * model.pageSize) .Take(model.pageSize) .ToList(); // 映射DTO var result = Mapper.Map<List<SeriesDTO>>(states);
临时应急方案(需注意性能)
如果暂时无法替换DistinctBy,可以先把去重后的结果加载到内存,再处理分页和总数:
// 先加载去重排序后的全量数据到内存 var filteredList = query.DistinctBy(q => q.Series.Name) .OrderBy(c => c.Series.RoleId.Value == (int)Type.Type1 ? 0 : 1) .ThenBy(c => c.Series.RoleId.Value == (int)Type.Type2 ? 0 : 1) .ThenBy(s => s.Series.RoleId) .OrderByDescending(a => a.Series.Date) .ThenByDescending(a => a.Series.Id) .ToList(); // 获取总数 var total = filteredList.Count; // 分页 var states = filteredList.Skip((model.pageNumber - 1) * model.pageSize) .Take(model.pageSize) .ToList(); // 映射DTO var result = Mapper.Map<List<SeriesDTO>>(states);
注意:这种方式会把所有符合条件的数据加载到内存,数据量大时会影响性能,仅适合小数据量场景。
内容的提问来源于stack exchange,提问作者Chattery
相关产品推荐
相关产品推荐

