EF查询.ThenInclude失效?优化后关联属性b.c为null问题
问题分析与解决方案
我来帮你拆解下这个问题——你的写法确实存在关键问题,导致ThenInclude没有生效,咱们一步步说清楚:
核心原因:投影(Select)会忽略Include/ThenInclude
EF Core中,Include和ThenInclude是用来加载原始实体的关联数据的,但当你使用Select进行投影操作时,EF会完全根据你指定的投影内容来生成SQL查询,之前设置的Include规则会被直接忽略。
你的查询最后用了Select(t => t.b),这意味着EF只会生成获取b表数据的SQL,不会自动带上b.c的关联,因为你没有在投影中明确要求加载这个关联属性,所以返回的b对象里c自然是null。
解决方法
根据你的需求,有几种更合适的写法:
方法1:先加载完整实体,再提取目标数据
如果不想改变太多原有结构,可以先加载包含所有关联的test实体,再从中提取b:
// 先加载test及其关联的a、b、b.c var testEntities = await test .Include(t => t.a) .Include(t => t.b) .ThenInclude(b => b.c) .Where(t => Users.Exists(u => u.Id == t.a.Id)) .ToListAsync(); // 从加载好的实体中提取b列表 var bList = testEntities.Select(t => t.b).ToList();
这种写法简单直接,但缺点是会加载test和a的数据,可能有额外的性能开销。
方法2:在投影中明确引用关联属性
更高效的方式是在Select里直接包含b.c,让EF知道需要加载这个关联:
var bList = await test .Where(t => Users.Exists(u => u.Id == t.a.Id)) // 投影时同时引用b和b.c,EF会自动加载关联 .Select(t => new { B = t.b, C = t.b.c // 这里引用c,EF会将其与B关联起来 }) .ToListAsync() // 最后提取B即可,此时B的C属性已经被加载 .Select(result => result.B) .ToList();
方法3:直接查询b实体(推荐)
如果你的最终目标就是获取b列表,不如直接从b的DbSet出发查询,这样更符合EF的设计,也更高效:
// 假设你的DbContext中B的DbSet名为Bs var bList = await dbContext.Bs .Include(b => b.c) // 直接为B加载关联的c // 通过test表的关联条件过滤 .Where(b => dbContext.test.Any(t => t.b.Id == b.Id && Users.Exists(u => u.Id == t.a.Id))) .ToListAsync();
额外性能提示
注意你用的Users.Exists(u => u.Id == t.a.Id)——如果Users是数据库中的DbSet(而非内存集合),建议换成Users.Any(u => u.Id == t.a.Id),因为Exists是内存集合的方法,会导致EF先把所有test数据拉到内存再过滤,这很可能是你之前查询复杂度高的原因之一。换成Any的话,EF会生成对应的SQL过滤条件,直接在数据库层面完成筛选,性能会大幅提升。
内容的提问来源于stack exchange,提问作者user6728767




