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

EntityFramework多ThenInclude关联查询过慢,求优化方案

优化Entity Framework查询性能的方案

你的问题里多次对User使用Include再ThenInclude,虽然EF Core会自动合并同一父级的关联加载请求,但写法可以更简洁,不过性能慢的根源大概率不是ThenInclude的次数,而是以下几个常见问题,对应解决方案如下:

1. 合并重复的父级关联加载写法

不需要重复Include(x => x.User),可以链式处理同一个父级的多个子集合(EF Core会自动合并为一次关联查询),优化后的写法如下:

var query = _postUserRepository.TableNoTracking
    .Where(x => x.User.Name.Contains(name))
    .Where(x => x.User.Email.Contains(email))
    .Where(x => x.JobPostId == jobPostId)
    .Where(x => x.JobPost.ExpiresAt > expiredDate)
    .Where(x => x.JobPost.UserId == userId)
    .Include(x => x.User)
        .ThenInclude(u => u.Languages)
    .Include(x => x.User)
        .ThenInclude(u => u.Skills)
    .Include(x => x.User)
        .ThenInclude(u => u.Educations)
    .Include(x => x.User)
        .ThenInclude(u => u.Resumes)
    .Include(x => x.User)
        .ThenInclude(u => u.Talent);

var list = query.Take(20).ToList();

注:EF Core会自动将多个针对User的Include合并,不会生成重复的JOIN语句,这一步主要是代码整洁性优化,对性能影响不大。

2. 解决笛卡尔积导致的数据膨胀问题

当你同时加载多个子集合(比如Languages、Skills等)时,EF默认会生成多表JOIN的SQL,这会导致笛卡尔积——比如User有3个Languages和2个Skills,就会生成6条重复的User数据,后续EF需要在内存中合并这些数据,这会大幅增加内存占用和处理时间。

解决方法是使用拆分查询(EF Core 5.0+支持),让EF为每个子集合单独生成查询,避免笛卡尔积:

var list = query.AsSplitQuery().Take(20).ToList();

3. 投影到DTO,只加载需要的字段

如果不需要使用实体的所有属性和关联集合的全部字段,直接投影到自定义的DTO(数据传输对象)是提升性能最有效的方式之一,这样EF只会查询你需要的字段,减少数据传输量和内存消耗:

public class PostUserDto
{
    public int Id { get; set; }
    // 只保留需要的PostUser字段
    public UserDto User { get; set; }
}

public class UserDto
{
    public string Name { get; set; }
    public string Email { get; set; }
    public List<LanguageDto> Languages { get; set; }
    // 只保留需要的User和关联集合字段
}

// 查询时直接投影
var list = _postUserRepository.TableNoTracking
    .Where(x => x.User.Name.Contains(name))
    .Where(x => x.User.Email.Contains(email))
    .Where(x => x.JobPostId == jobPostId)
    .Where(x => x.JobPost.ExpiresAt > expiredDate)
    .Where(x => x.JobPost.UserId == userId)
    .Select(x => new PostUserDto
    {
        Id = x.Id,
        User = new UserDto
        {
            Name = x.User.Name,
            Email = x.User.Email,
            Languages = x.User.Languages.Select(l => new LanguageDto { /* 按需定义字段 */ }).ToList(),
            Skills = x.User.Skills.Select(s => new SkillDto { /* 按需定义字段 */ }).ToList()
            // 其他需要的关联集合
        }
    })
    .Take(20)
    .ToList();

4. 优化过滤条件的索引

你的查询中使用了User.Name.Contains(name)和User.Email.Contains(email),这类模糊查询(LIKE '%xxx%')无法利用常规的非聚集索引,会导致全表扫描。可以:

  • 如果是SQL Server,创建全文索引,并使用EF的EF.Functions.FreeText或EF.Functions.Contains来利用全文索引;
  • 对JobPostId、JobPost.ExpiresAt、JobPost.UserId这些精确匹配或范围查询的字段创建非聚集索引,加速WHERE条件的过滤。

5. 验证是否需要所有关联集合

检查业务逻辑是否真的需要同时加载Languages、Skills、Educations、Resumes、Talent所有集合,如果某些集合不是必须的,可以移除对应的ThenInclude,减少关联查询的开销。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 07:27:50