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

EF Core含Include的Linq查询过慢问题求助

EF Core Include关联查询性能问题排查方案

问题背景

执行以下Linq查询时,包含Include(t => t.ExerciseSteps)后耗时超1分钟,但生成的左连接SQL在SQL Server直接执行速度极快。移除Include或添加AsNoTracking均无改善:

_db.TrainingsExercises
   .Include(t => t.ExerciseSteps)
   .Where(te => te.UserId == userId);

涉及模型与上下文配置如下:

TrainingExercise模型

public class TrainingExercise
{
    [Key]
    public Guid Id { get; set; }
    public Guid UserId { get; set; }
    public string Name { get; set; }
    public bool IsPublic { get; set; }
    [ForeignKey(nameof(UserId))]
    public User User { get; set; }
    public ICollection<TrainingExerciseStep> ExerciseSteps { get; set; }
    public ICollection<TeamTrainingExercise>? TeamTrainings { get; set; }
}

TrainingExerciseStep模型

public class TrainingExerciseStep
{
    [Key]
    public Guid Id { get; set; }
    public Guid TrainingExerciseId { get; set; }
    public int Order { get; set; }
    public string? Name { get; set; }
    public string? Image { get; set; }
    [ForeignKey(nameof(TrainingExerciseId))]
    public TrainingExercise TrainingExercise { get; set; }
    public List<TrainingExerciseStepComponents> ExerciseStepComponents { get; set; }
}

DbContext配置

public DbSet<TrainingExercise> TrainingsExercises { get; set; }
public DbSet<TrainingExerciseStep> TrainingExerciseSteps { get; set; }

builder.Entity<TrainingExercise>(entity =>
{
    entity.HasMany(u => u.ExerciseSteps)
          .WithOne(s => s.TrainingExercise);
});

排查与解决方向

1. 优化实体映射开销

SQL执行快但Linq查询慢,大概率是EF Core将查询结果映射到实体的过程存在瓶颈,尤其是返回数据量较大时:

  • 统计该userId对应的TrainingExercise总数,以及关联的ExerciseSteps总条数,判断是否因数据量过大导致内存映射耗时。
  • 改用手动投影,只加载需要的字段,减少内存占用和映射时间:
    _db.TrainingsExercises
       .Where(te => te.UserId == userId)
       .Select(te => new 
       {
           te.Id,
           te.Name,
           ExerciseSteps = te.ExerciseSteps.Select(es => new 
           {
               es.Id,
               es.Order,
               es.Name
           }).ToList()
       });
    

2. 排查隐性关联加载

注意TrainingExerciseStep包含ExerciseStepComponents集合,EF Core可能触发延迟加载(即使未显式Include),导致额外查询开销:

  • 开启EF Core日志,检查是否有加载ExerciseStepComponents的额外SQL语句。
  • 关闭延迟加载:在DbContext构造函数中添加this.ChangeTracker.LazyLoadingEnabled = false;,或移除ExerciseStepComponents的virtual修饰符(如果之前标记了)。

3. 验证SQL执行计划一致性

EF Core生成的参数化SQL与手动执行的SQL可能因参数嗅探等问题导致执行计划不同:

  • 用ToQueryString()获取EF Core生成的完整SQL,在SSMS中使用相同参数值执行,对比执行计划是否一致。
  • 确保TrainingExerciseStep表的TrainingExerciseId字段存在索引,加速关联查询:
    CREATE NONCLUSTERED INDEX IX_TrainingExerciseStep_TrainingExerciseId ON TrainingExerciseStep (TrainingExerciseId);
    

4. 使用拆分查询

EF Core默认用左连接返回包含重复主表数据的大结果集,映射时开销极高。改用AsSplitQuery()拆分查询为两次独立请求(先查主表,再查关联表):

_db.TrainingsExercises
   .Include(t => t.ExerciseSteps)
   .Where(te => te.UserId == userId)
   .AsSplitQuery();

5. 升级EF Core版本

部分旧版本EF Core在关联查询映射逻辑上存在性能缺陷,尝试升级到最新稳定版(如EF Core 7+),看是否解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 09:46:23