EF Core 的 Single Query(单查询)并不是一个显式调用的方法,而是指 EF Core 在使用
Include进行预加载(Eager Loading)时,默认生成的 **一条 SQL JOIN 查询** —— 也就是把主表和关联表的数据一次性查出来,避免 N+1 查询问题。它本质是 EF Core 的默认行为,但需要正确使用才能真正生效。
什么时候会生成 Single Query?
当你用
Include+
ThenInclude加载关联数据,并且所有导航路径都支持 JOIN(比如一对一、一对多),EF Core 通常会翻译成一条带
LEFT JOIN或
INNER JOIN的 SQL 语句。 一对多关系(如 Blog → Posts)→ 生成 LEFT JOIN 多对一/一对一(如 Post → Author)→ 默认 INNER JOIN,除非配置为可空 多个同级
Include(如
.Include(x => x.Author).Include(x => x.Tags))→ 仍是一条 SQL,但可能产生笛卡尔积(需留意)
怎么确保真正用上 Single Query?
关键不是“怎么开启”,而是“怎么不破坏”它:
别在Include后接
Where或
OrderBy在子集合上(例如
.Include(b => b.Posts).Where(b => b.Posts.Any(...))),这会让 EF Core 切换到 Split Query 模式(多条 SQL)或报错 避免在
Include链中混用过滤条件(如
.Include(b => b.Posts.Where(p => p.IsPublished))),EF Core 6+ 支持这种写法,但底层仍是 Single Query;EF Core 5 及更早版本不支持,会出错 如果发现生成了多条 SQL,可以用
context.Database.Log或 SQL Server Profiler 查看实际执行语句
合并多个实体查询的替代方案:Split Queries
当 Single Query 因笛卡尔积导致性能下降(比如一对多再加一对多),EF Core 提供了
AsSplitQuery()显式启用拆分查询: 它会把一个含多个
Include的查询,拆成多条独立 SQL(每条查一个表) 适合大数据量、深度关联但又不想膨胀结果集的场景 用法:
context.Blogs.Include(b => b.Posts).ThenInclude(p => p.Author).AsSplitQuery().ToList()
不推荐但有时可行的“手动合并”方式
如果你真想把几个不同实体的查询“合并在一次数据库往返中”,EF Core 本身不支持跨 DbContext 或跨 DbSet 的 UNION 类型合并。可行思路有:
用原始 SQL(context.Blogs.FromSqlRaw("SELECT ... UNION SELECT ...")),但要自己映射结果
用 JoinLINQ 写法代替
Include,只取需要字段(更轻量,无实体图) 业务层合并:分别查
Blogs、
Posts、
Authors,再用 C# 关联(适合缓存友好或复杂筛选场景)
基本上就这些。Single Query 是默认且高效的起点,重点是别无意中把它“切开”——理解 Include 的边界,比记住语法更重要。
