.NET Core 2.2升级至3.1时EF Core LINQ查询无法翻译报错如何解决
问题根因
EF Core从3.0版本开始默认禁用了隐式客户端评估机制,2.2版本中无法被翻译成SQL的查询片段会自动拉到内存执行,3.0+版本遇到无法翻译的逻辑会直接抛出该异常,避免全表加载等性能问题。
最优解决方案:重写查询为可翻译形式
该方案所有逻辑都在数据库端执行,性能最优,仅需要调整查询中无法翻译的片段:
需要调整的两处逻辑
Select中的string.Format无法被EF Core 3.1翻译,替换为字符串拼接即可- 不必要的显式类型转换
(Nullable<int>)e.Key.Targeted_Year.Value会导致翻译失败,直接使用分组键的原始字段即可
重写后的查询示例
var result = await dbContext.FactInspection .Where(f => f.Media_Id == filter.MediaId && f.Targeted_Date != null && f.Targeted_Date.Value.Date >= filter.StartDate.Date && f.Targeted_Date.Value.Date <= filter.EndDate.Date) .GroupBy(f => new { f.Targeted_Year, f.Targeted_Quarter, f.Targeted_Quater_Range, Phase = f.Phase == "Completed" ? "Completed" : "Not Completed" }) .Select(e => new TargetDashboardResponse{ Targeted_Year = e.Key.Targeted_Year, Quarter = e.Key.Targeted_Quarter, Name = $"{e.Key.Targeted_Quater_Range} ({e.Key.Targeted_Year})", Phase = e.Key.Phase, Count = e.Count() }) .OrderBy(e0 => e0.Targeted_Year) .ToListAsync();
备选方案:显式开启客户端评估
如果查询逻辑复杂难以重写,且过滤后的数据量较小,可以在Where过滤完成后插入AsAsyncEnumerable/ToListAsync,将过滤后的数据集拉到内存执行后续分组、投影逻辑:
var result = await dbContext.FactInspection .Where(f => f.Media_Id == filter.MediaId && f.Targeted_Date != null && f.Targeted_Date.Value.Date >= filter.StartDate.Date && f.Targeted_Date.Value.Date <= filter.EndDate.Date) // 过滤完成后加载到内存,后续逻辑在客户端执行 .AsAsyncEnumerable() .GroupBy(f => new { f.Targeted_Year, f.Targeted_Quarter, f.Targeted_Quater_Range, Phase = f.Phase == "Completed" ? "Completed" : "Not Completed" }) .Select(e => new TargetDashboardResponse{ Targeted_Year = e.Key.Targeted_Year, Quarter = e.Key.Targeted_Quarter, Name = string.Format("{0} ({1})", e.Key.Targeted_Quater_Range, e.Key.Targeted_Year), Phase = e.Key.Phase, Count = e.Count() }) .OrderBy(e0 => e0.Targeted_Year) .ToListAsync();
注意:不要在
Where之前调用AsAsyncEnumerable/ToList,否则会加载全表数据到内存过滤,造成严重性能问题。
排查方法
- 逐段注释查询中的投影、分组逻辑,定位到无法翻译的片段
- 开启EF Core的Debug级日志,可查看具体的翻译失败节点
内容的提问来源于stack exchange,提问作者Pradeepkumar Raman
相关产品推荐
相关产品推荐

