重构LINQ查询并返回分组后的单个聚合结果
重构你的LINQ查询,返回单个聚合结果
首先我得先拆解一下你这段查询的核心意图:你想要筛选出IsValid为true、且CreatedOn落在[endDate, startDate)区间内的Application记录,然后计算这些记录的ApplicationTime和DecisionTime的平均值(四舍五入取整),最终返回包含这两个平均值的单个结果。
原代码的写法过于繁琐,而且用GroupBy((x) => (int)1)这种方式把所有数据归为一组,再用First取结果,其实完全可以简化。下面给你两种重构方案:
方案一:去掉不必要的分组(推荐)
既然你是对所有符合条件的数据计算整体平均值,根本不需要分组操作,直接对筛选后的序列计算聚合即可,代码简洁很多:
如果你使用的是EF Core等支持LINQ to Entities的ORM,还能让ORM帮你转换成高效的SQL聚合查询:
var result = r.Find<Application>(x => x.IsValid && x.CreatedOn < startDate && x.CreatedOn >= endDate) .GroupBy(x => 1) // 若不需要分组语义,这一行可以直接去掉,直接用下面的聚合逻辑 .Select(g => new { AvgApplicationTime = (int)Math.Round(g.Average(x => x.ApplicationTime)), AvgDecisionTime = (int)Math.Round(g.Average(x => x.DecisionTime)) }) .SingleOrDefault(); // 确保返回单个结果,无匹配数据时返回null
如果是内存中的集合操作,也可以用Aggregate手动计算,避免潜在的空序列异常:
var result = r.Find<Application>(x => x.IsValid && x.CreatedOn < startDate && x.CreatedOn >= endDate) .Select(x => new { x.ApplicationTime, x.DecisionTime }) .Aggregate( new { SumAppTime = 0, SumDecTime = 0, Count = 0 }, (acc, item) => new { SumAppTime = acc.SumAppTime + item.ApplicationTime, SumDecTime = acc.SumDecTime + item.DecisionTime, Count = acc.Count + 1 }, acc => acc.Count == 0 ? new { AvgApplicationTime = 0, AvgDecisionTime = 0 } // 无数据时返回默认值 : new { AvgApplicationTime = (int)Math.Round((double)acc.SumAppTime / acc.Count), AvgDecisionTime = (int)Math.Round((double)acc.SumDecTime / acc.Count) } );
方案二:简化原分组查询的写法
如果你坚持要保留分组逻辑(比如后续可能扩展分组维度),可以把原代码的冗余写法大幅简化,提升可读性:
// 先把筛选条件抽出来,让逻辑更清晰 Expression<Func<Application, bool>> filterCondition = x => x.IsValid && x.CreatedOn < startDate && x.CreatedOn >= endDate; var result = r.Find(filterCondition) .GroupBy(x => 1) // 简化分组键写法,不需要强制转换为(int)1 .Select(g => new { AvgApplicationTime = (int)Math.Round(g.Average(x => x.ApplicationTime)), AvgDecisionTime = (int)Math.Round(g.Average(x => x.DecisionTime)) }) .FirstOrDefault(); // 获取唯一分组结果,无数据时返回默认值
关键改进点:
- 简化表达式结构:把嵌套的
Queryable静态方法调用改成流畅的方法链,可读性提升明显 - 去除冗余转换:
GroupBy(x => 1)已经足够,不需要强制转(int)1 - 明确处理边界情况:用
SingleOrDefault或FirstOrDefault替代原代码的First,避免无匹配数据时抛出异常 - 可选移除分组:如果不需要分组维度,直接计算聚合更高效,减少不必要的分组操作
内容的提问来源于stack exchange,提问作者John Ohara
相关产品推荐
相关产品推荐

