You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core 3.1中Include生成无过滤子查询全表扫描如何优化?

问题原因

你遇到的是EF Core 3.1版本的查询翻译器已知缺陷:处理一对多关联的Include+ThenInclude时,为了避免关联查询产生笛卡尔积,会将集合关联封装为独立子查询,但不会把主表的过滤条件下推到子查询内部,最终导致子查询全表扫描后再做关联,性能极差。这个问题在EF Core 5.0及以上版本已经被官方修复。

解决方案

方案1:优先升级EF Core版本

直接升级到EF Core 5.0或更高版本,不需要修改现有查询代码,翻译器会自动将主表的MeetingId == 1过滤条件下推到每个集合子查询中,不会出现全表扫描问题。

方案2:使用拆分查询(EF Core 3.1支持)

如果暂时不能升级大版本,可以在查询末尾加上AsSplitQuery()方法,让EF Core将多个关联加载拆分为多条独立的SQL语句执行,每条SQL都会携带过滤条件,避免全表扫描:

Meetings
  .Include(a => a.Document)
  .Include(a => a.Plan)
  .Include(a => a.User)
  .Include(a => a.Topics).ThenInclude(e => e.Extra)
  .Include(a => a.Components).ThenInclude(g => g.Extra)
  .Include(a => a.Recipients).ThenInclude(i => i.Info)
  .AsSplitQuery()
  .Single(a => a.MeetingId == 1)

方案3:使用显式加载

如果拆分查询不符合你的业务需求,可以改为先查主表实体,再手动加载关联集合,生成的SQL都会自动携带主表ID过滤条件:

// 先加载主表和一对一关联
var meeting = Meetings
  .Include(a => a.Document)
  .Include(a => a.Plan)
  .Include(a => a.User)
  .Single(a => a.MeetingId == 1);

// 手动加载每个一对多关联
dbContext.Entry(meeting)
  .Collection(a => a.Topics)
  .Query()
  .Include(e => e.Extra)
  .Load();

dbContext.Entry(meeting)
  .Collection(a => a.Components)
  .Query()
  .Include(g => g.Extra)
  .Load();

dbContext.Entry(meeting)
  .Collection(a => a.Recipients)
  .Query()
  .Include(i => i.Info)
  .Load();

方案4:使用投影查询

如果你不需要返回完整的Meeting实体,只需要用到部分字段,建议直接用Select做投影查询,不需要写Include,生成的SQL天然更精简,不会出现全表扫描问题:

var meetingData = Meetings
  .Where(a => a.MeetingId == 1)
  .Select(a => new 
  {
      a.MeetingId,
      a.Title,
      a.Document,
      a.Plan,
      a.User,
      Topics = a.Topics.Select(t => new { t, t.Extra }).ToList(),
      Components = a.Components.Select(c => new { c, c.Extra }).ToList(),
      Recipients = a.Recipients.Select(r => new { r, r.Info }).ToList()
  })
  .Single();

内容的提问来源于stack exchange,提问作者Antediluvian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 08:09:03