You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

EF查询.ThenInclude失效?优化后关联属性b.c为null问题

问题分析与解决方案

我来帮你拆解下这个问题——你的写法确实存在关键问题,导致ThenInclude没有生效,咱们一步步说清楚:

核心原因:投影(Select)会忽略Include/ThenInclude

EF Core中,IncludeThenInclude是用来加载原始实体的关联数据的,但当你使用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();

这种写法简单直接,但缺点是会加载testa的数据,可能有额外的性能开销。

方法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

火山引擎 最新活动