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

