如何简化含多层子查询的Linq语句并提升性能?
优化OnBase Hyland API对接的LINQ语句及性能提升方案
问题背景
对接OnBase Hyland API与数据库筛选字段列表时,现有LINQ语句存在多层嵌套遍历导致性能低下,且fieldsList1.AddRange(fieldAsAttribute.ToList())无法正常工作(返回的是嵌套匿名类型集合而非目标Models.Field集合),需要简化语句并提升性能。
相关类定义
数据库字段实体类
public partial class field { public long field_id { get; set; } public string field_name { get; set; } public bool kw_flag { get; set; } public string kw_name { get; set; } public bool wv_flag { get; set; } public string wv_app { get; set; } public string wv_class { get; set; } public string wv_attr { get; set; } public string wv_dataset_name { get; set; } public string wv_filter_app { get; set; } }
OnBase Hyland核心对象类
// Application相关定义 public class Application { public ClassList Classes; public FilterList Filters; public long ID { get; internal set; } public string Name { get; internal set; } } // Class相关定义 public class Class { public AttributeList Attributes; public long ID { get; internal set; } public string Name { get; internal set; } internal Application WVApplication { get; set; } } // Attribute相关定义 public sealed class Attribute { public Class Class => base.Application.WorkView.GetClassByID(ClassID); internal long ClassID { get; set; } } // 容器类属性 public KeywordTypeList KeywordTypes => _keywordTypeList ?? (_keywordTypeList = new KeywordTypeListImplementation(base.Application)); public ApplicationList Applications => _applications ?? (_applications = new ApplicationListImplementation(base.Application));
优化方案及代码实现
1. 核心优化思路
- 扁平化集合:使用
SelectMany替代多层嵌套遍历,直接提取目标Models.Field对象 - 预构建索引:将频繁查询的对象(Application、Class、Filter)转为字典/Lookup,避免重复遍历
- 延迟执行:减少不必要的
ToList()调用,仅在最终需要物化集合时执行
2. 优化后的完整代码
// 1. 预构建索引,避免重复遍历查询 var appDict = app.WorkView.Applications.ToDictionary(a => a.Name, a => a); var classLookup = app.WorkView.Applications .SelectMany(a => a.Classes, (app, cls) => new { AppName = app.Name, Class = cls }) .ToLookup(x => (x.AppName, x.Class.Name), x => x.Class); // 预构建每个Application的Filter字典 var appFilterDict = app.WorkView.Applications.ToDictionary( a => a.Name, a => a.Filters.ToDictionary(f => f.Name, f => f.ID) ); var fieldsList = new List<Models.Field>(); // 2. 简化LINQ查询,直接扁平化提取目标对象 var targetFields = filteredFields // 仅处理wv_flag为true的字段(按需启用) .Where(f => f.wv_flag) // 匹配对应的Application .Where(f => appDict.ContainsKey(f.wv_app)) .Select(f => new { FilteredField = f, App = appDict[f.wv_app] }) // 匹配对应的Class .Where(x => classLookup.ContainsKey((x.App.Name, x.FilteredField.wv_class))) .SelectMany(x => classLookup[(x.App.Name, x.FilteredField.wv_class)], (x, cls) => new { x.FilteredField, x.App, Class = cls }) // 匹配对应的Attribute .Where(x => x.Class.Attributes.Any(attr => attr.Name == x.FilteredField.wv_attr)) // 构建目标Models.Field对象 .Select(x => new Models.Field { FieldId = x.FilteredField.field_id, Name = x.FilteredField.field_name, HasDropdown = true, // 从预构建的字典中直接获取FilterID,避免重复查询 DropdownId = appFilterDict[x.App.Name].TryGetValue(x.FilteredField.wv_filter, out var filterId) ? filterId : 0, // 或根据业务设置默认值 DropdownType = Models.Field.DropdownTypeEnum.FilterEnum }); // 3. 直接添加到列表,解决AddRange失效问题 fieldsList.AddRange(targetFields);
3. 关键优化点说明
- 预构建索引:将Application、Class、Filter转为字典/Lookup后,查询时间复杂度从O(n)降为O(1),大幅减少重复遍历开销
- 扁平化查询:通过
SelectMany直接将多层嵌套的集合转为单层Models.Field集合,无需手动三层foreach,代码更简洁且性能更高 - 避免空引用风险:使用
TryGetValue替代SingleOrDefault(),防止Filter不存在时出现空引用异常 - 减少不必要的物化:仅在最终
AddRange时才执行查询,充分利用LINQ的延迟执行特性
内容的提问来源于stack exchange,提问作者user575219
相关产品推荐
相关产品推荐

