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

C# Linq查询优化:避免多层Include,按需获取属性

优化EF Core查询性能的方案

你的核心问题是使用Include加载了完整实体的所有属性,即使只需要其中一部分,这会导致数据库查询冗余数据、IO开销增大,最终拖慢执行速度。最有效的优化方式是用投影(Select)替代Include,只选择业务需要的字段,生成更精简的SQL查询。

具体优化方法

1. 使用投影选择所需属性

通过Select直接指定需要的字段(包括嵌套导航属性的字段),EF Core会自动生成仅包含这些字段的SQL,避免加载冗余数据。你可以选择用匿名类型(快速临时查询)或自定义DTO类(需要跨层传递数据时更规范)。

示例1:用匿名类型快速实现

results = await _dbContext.Table0
    .Where(c => c.attribute == 23)
    .Select(c => new
    {
        // Table0自身需要的属性
        Id = c.Id,
        Attribute = c.attribute,

        // Table1的2个目标属性 + 关联的Table2、Table4字段
        Table1 = new
        {
            c.table1.Prop1,
            c.table1.Prop2,
            Table2 = new
            {
                c.table1.table2.TargetProp,
                Table4 = new
                {
                    c.table1.table2.table3.table4.PropX,
                    c.table1.table2.table3.table4.PropY
                }
            }
        },

        // 通过Table0的Table3关联获取Table4的字段(无需Table3自身属性)
        Table4FromTable3 = new
        {
            c.table3.table4.PropX,
            c.table3.table4.PropY
        },

        // Table5/6/7的目标属性
        Table5Prop = c.table5.RequiredProp,
        Table6Prop = c.table6.KeyProp,
        Table7Prop = c.table7.StatusProp
    })
    .ToListAsync();

示例2:用自定义DTO类(适合业务场景复用)

先定义对应的数据传输对象:

// 顶层DTO
public class Table0QueryResult
{
    public int Id { get; set; }
    public int Attribute { get; set; }
    public Table1Dto Table1 { get; set; }
    public Table4Dto Table4FromTable3 { get; set; }
    public string Table5Prop { get; set; }
    public int Table6Prop { get; set; }
    public bool Table7Prop { get; set; }
}

public class Table1Dto
{
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
    public Table2Dto Table2 { get; set; }
}

public class Table2Dto
{
    public string TargetProp { get; set; }
    public Table4Dto Table4 { get; set; }
}

public class Table4Dto
{
    public decimal PropX { get; set; }
    public DateTime PropY { get; set; }
}

再编写查询代码:

results = await _dbContext.Table0
    .Where(c => c.attribute == 23)
    .Select(c => new Table0QueryResult
    {
        Id = c.Id,
        Attribute = c.attribute,
        Table1 = new Table1Dto
        {
            Prop1 = c.table1.Prop1,
            Prop2 = c.table1.Prop2,
            Table2 = new Table2Dto
            {
                TargetProp = c.table1.table2.TargetProp,
                Table4 = new Table4Dto
                {
                    PropX = c.table1.table2.table3.table4.PropX,
                    PropY = c.table1.table2.table3.table4.PropY
                }
            }
        },
        Table4FromTable3 = new Table4Dto
        {
            PropX = c.table3.table4.PropX,
            PropY = c.table3.table4.PropY
        },
        Table5Prop = c.table5.RequiredProp,
        Table6Prop = c.table6.KeyProp,
        Table7Prop = c.table7.StatusProp
    })
    .ToListAsync();

优化优势说明

  • 减少数据库IO:生成的SQL仅查询你指定的字段,避免加载整个表的冗余数据,大幅降低数据库查询耗时。
  • 降低内存占用:只加载需要的属性,减少应用端内存消耗。
  • 避免重复关联:原代码中重复Include(c => c.table1)的问题会自动消失,投影逻辑更简洁。

注意事项

  • 若导航属性可能为null,可使用?.运算符避免空引用,EF Core会自动转换为LEFT JOIN,比如c.table1?.Prop1。
  • 若需要后续修改并保存实体,投影到DTO/匿名类型后实体不会被EF跟踪,这种情况可结合AsNoTracking进一步优化查询性能(因为不需要跟踪时,EF会减少额外开销)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:50:23