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

使用EF Core(.NET 6)Include()加载大量数据时如何解决性能问题?

EF Core 大数量关联数据加载的性能优化方案

问题场景

我正在开发基于.NET 6的Entity Framework Core应用,调用GetQuestionByTest()方法检索数据时遇到性能问题。需要加载与TblQuestionTest实体关联的TblMultipleChoices数据,但数据量较大导致应用响应延迟。

问题代码:

public IEnumerable<TblQuestionTest> GetQuestionByTest(int tesId)
{
    return context.TblQuestionTests
                  .Include(e => e.Test)
                  .Include(e => e.TblMultipleChoices)
                  .Where(e => e.TestId == tesId);
}

选择题模型(scaffold生成):

public partial class TblMultipleChoice
{
    public int MultipleChoiceId { get; set; }

    public string? MultipleChoiceDesc { get; set; }

    public int QuestionId { get; set; }

    public string Score { get; set; } = null!;

    public virtual TblQuestionTest Question { get; set; } = null!;
}

优化策略与实现

1. 投影查询:只加载需要的字段

默认Include会加载关联实体的所有字段,很多属性可能是业务不需要的。用Select手动指定所需字段,能大幅减少数据库返回的数据量,降低传输和序列化开销。

示例代码:

// 可以定义DTO类替代匿名类,实现强类型返回
public class QuestionTestDto
{
    public int QuestionTestId { get; set; }
    public string QuestionContent { get; set; } = null!;
    public TestDto Test { get; set; } = null!;
    public List<MultipleChoiceDto> Choices { get; set; } = new();
}

public class TestDto { public int TestId { get; set; } public string TestName { get; set; } = null!; }
public class MultipleChoiceDto { public int Id { get; set; } public string? Desc { get; set; } public string Score { get; set; } = null!; }

public IEnumerable<QuestionTestDto> GetQuestionByTest(int tesId)
{
    return context.TblQuestionTests
                  .Where(e => e.TestId == tesId)
                  .Select(e => new QuestionTestDto
                  {
                      QuestionTestId = e.QuestionTestId,
                      QuestionContent = e.QuestionContent,
                      Test = new TestDto { TestId = e.Test.TestId, TestName = e.Test.TestName },
                      Choices = e.TblMultipleChoices.Select(mc => new MultipleChoiceDto
                      {
                          Id = mc.MultipleChoiceId,
                          Desc = mc.MultipleChoiceDesc,
                          Score = mc.Score
                      }).ToList()
                  });
}

2. 分页加载:避免一次性返回全部数据

如果前端不需要一次性展示所有题目,分页是最直接的优化手段。通过Skip和Take控制每次返回的数据量,减轻数据库和应用的压力。

示例代码:

public IEnumerable<TblQuestionTest> GetQuestionByTest(int tesId, int pageIndex, int pageSize)
{
    return context.TblQuestionTests
                  .Include(e => e.Test)
                  .Include(e => e.TblMultipleChoices)
                  .Where(e => e.TestId == tesId)
                  .Skip((pageIndex - 1) * pageSize)
                  .Take(pageSize);
}

3. 使用拆分查询(Split Query)

EF Core默认用笛卡尔积方式查询关联数据,当TblMultipleChoices数量较多时,TblQuestionTest的数据会被重复返回,导致数据量暴增。AsSplitQuery()可以把查询拆分成多个独立SQL语句,分别查询主实体和关联实体,避免数据重复。

示例代码:

public IEnumerable<TblQuestionTest> GetQuestionByTest(int tesId)
{
    return context.TblQuestionTests
                  .Include(e => e.Test)
                  .Include(e => e.TblMultipleChoices)
                  .Where(e => e.TestId == tesId)
                  .AsSplitQuery(); // 添加拆分查询
}

4. 数据库索引优化

检查数据库中TblQuestionTests表的TestId字段、TblMultipleChoices表的QuestionId字段是否创建索引。无索引时数据库会做全表扫描,数据量大时速度极慢。

通过EF Core迁移添加索引:

// 在TblQuestionTests的配置类中添加
builder.HasIndex(e => e.TestId);

// 在TblMultipleChoices的配置类中添加
builder.HasIndex(e => e.QuestionId);

执行迁移命令:

Add-Migration AddIndexesForPerformance
Update-Database

5. 延迟加载(谨慎使用)

如果不是每次调用都需要加载TblMultipleChoices,可以开启延迟加载。它会在第一次访问TblMultipleChoices属性时才查询数据库,但要注意N+1问题(每个主实体都会触发一次单独查询)。

开启方式:在DbContext构造函数中设置:

public YourDbContext(DbContextOptions<YourDbContext> options) : base(options)
{
    ChangeTracker.LazyLoadingEnabled = true;
}

注:你的实体导航属性已经用virtual修饰,满足延迟加载的前提条件。此方案仅适合偶尔需要关联数据的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 18:25:57