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

C# LINQ实现GroupBy后按条件选择记录替代foreach循环

LINQ 关联集合扁平化查询改写方案

你自行改写的LINQ版本存在几个明显问题,会导致运行报错或者性能损耗:

  • 空引用风险:关联联系人集合的默认返回值设为null,后续直接调用x.Contact.Any()时,只要Contact为null就会抛出空引用异常,和原始foreach逻辑的空值处理规则不匹配。
  • 去重逻辑冗余:用GroupBy取第一条的方式实现联系人去重完全没有必要,直接用Distinct()即可,EF/EF Core可以直接将其翻译为SQL的DISTINCT关键字,在数据库侧完成去重,性能远高于内存分组。
  • 语法错误:Key1赋值行多写了一个多余的逗号,会直接导致编译失败。
  • 查询执行时机不合理:投影完匿名类型就立刻调用ToList(),会把所有中间结果全量拉到内存再做扁平化处理,完全可以把数据过滤、投影的逻辑放在数据库侧执行,减少数据传输量和内存占用。

下面是完全对齐原始foreach逻辑、无显式循环的优化后实现:

return _context.MainRecordTable
    .Where(h => h.id == Id)
    .Select(lev => new 
    {
        // 空链路判断后取有效联系人,用空集合替代null作为默认值,避免后续空引用
        Contacts = (lev.basetable2 != null 
                            && lev.basetable2.basetable3 != null 
                            && lev.basetable2.basetable3.basetable6 != null
                            &&  lev.basetable2.basetable3.basetable6.Any(h=>h.contact != null)
                            ? lev.basetable2.basetable3.basetable6.Where(h=>h.contact != null).Select(h=>h.contact).Distinct() 
                            : Enumerable.Empty<Contact>()),
        Key1 = lev.basetable5 != null ? lev.basetable5.Id : null,
        Key2 = lev.basetable2 != null && lev.basetable2.basetable3 != null && lev.basetable2.basetable3.basetable4 != null ?
                            lev.basetable2.basetable3.basetable4.id : null
    })
    .Distinct()
    // 用SelectMany一步完成嵌套循环的扁平化逻辑
    .SelectMany(row => 
        // 无有效联系人时返回单条仅带Key1、Key2的记录
        !row.Contacts.Any() 
            ? new List<ResultModel>
            {
                new ResultModel { Key1 = row.Key1, Key2 = row.Key2 }
            }
            // 有有效联系人时,每个联系人生成一条带ContactKey的记录
            : row.Contacts.Select(contact => new ResultModel
            {
                Key1 = row.Key1,
                Key2 = row.Key2,
                ContactKey = contact.id
            })
    )
    .ToList();

补充说明:

  • 如果项目使用C# 6及以上版本,可以用?.空条件运算符简化多层null判断的写法,EF Core 2.1+版本可以正常将该语法翻译为对应的SQL空判断逻辑,不会有额外性能损耗。
  • 关联集合默认返回空集合而非null是LINQ查询的通用最佳实践,可以避免绝大多数集合遍历场景下的空引用异常,不需要在后续逻辑中重复做null判断。
  • EF Core 3.0及以上版本可以正常翻译上述SelectMany逻辑为标准SQL(通过LEFT JOIN + 判别列的方式实现),不需要把中间数据拉到内存处理,性能远高于手写foreach循环的内存拼接实现。
  • 如果确实需要在内存中处理中间逻辑,只需要在Distinct()后调用AsEnumerable()即可,不要过早执行查询拉取全量数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 18:09:38