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
相关产品推荐
相关产品推荐

