EF中GroupBy()未按预期工作,仅内存分组才生效
LINQ分组查询结果异常问题排查与解决
我编写了如下LINQ查询,期望按年月分组得到多条统计结果,但实际仅返回1条:
var results = await DbContext.Trucks .Where(t => t.Departure.HasValue) .Select(t => new { t.Departure!.Value.Year, t.Departure!.Value.Month, Quantity = t.InboundQuantity + t.ToTransfers.Sum(x => x.Quantity) - t.FromTransfers.Sum(x => x.Quantity) }) .GroupBy(t => new { t.Year, t.Month }) .ToDictionaryAsync(g => new DateTime(g.Key.Year, g.Key.Month, 1), g => g.Sum(x => x.Quantity));
执行结果:
results Count = 1 + [0] {[{7/1/2021 12:00:00 AM}, 3842405714]}
但如果先通过ToList()将所有数据加载到内存,再执行分组逻辑,就能得到预期的27条结果:
var test = DbContext.Trucks .Where(t => t.Departure.HasValue) .Select(t => new { t.Departure!.Value.Year, t.Departure!.Value.Month, Quantity = t.InboundQuantity + t.ToTransfers.Sum(x => x.Quantity) - t.FromTransfers.Sum(x => x.Quantity) }) .ToList(); var test2 = test .GroupBy(t => new { t.Year, t.Month }) .ToDictionary(g => new DateTime(g.Key.Year, g.Key.Month, 1), g => g.Sum(x => x.Quantity));
执行结果:
test2 Count = 27 + [0] {[{1/1/2022 12:00:00 AM}, 95309170]} + [1] {[{2/1/2022 12:00:00 AM}, 79737674]} + [2] {[{3/1/2022 12:00:00 AM}, 173875538]} + [3] {[{7/1/2021 12:00:00 AM}, 125219196]} + [4] {[{8/1/2021 12:00:00 AM}, 110993954]} + [5] {[{10/1/2021 12:00:00 AM}, 129282632]} + [6] {[{11/1/2021 12:00:00 AM}, 160927222]} + [7] {[{12/1/2021 12:00:00 AM}, 117813444]} + [8] {[{9/1/2021 12:00:00 AM}, 26260420]} + [9] {[{4/1/2022 12:00:00 AM}, 161695966]} + [10] {[{5/1/2022 12:00:00 AM}, 184781001]} + [11] {[{6/1/2022 12:00:00 AM}, 146985217]} + [12] {[{7/1/2022 12:00:00 AM}, 84260160]} + [13] {[{8/1/2022 12:00:00 AM}, 89769875]} + [14] {[{10/1/2022 12:00:00 AM}, 187958697]} + [15] {[{11/1/2022 12:00:00 AM}, 175034225]} + [16] {[{9/1/2022 12:00:00 AM}, 108421771]} + [17] {[{1/1/2023 12:00:00 AM}, 179720773]} + [18] {[{12/1/2022 12:00:00 AM}, 110000415]} + [19] {[{2/1/2023 12:00:00 AM}, 210461751]} + [20] {[{3/1/2023 12:00:00 AM}, 232239876]} + [21] {[{4/1/2023 12:00:00 AM}, 176482465]} + [22] {[{5/1/2023 12:00:00 AM}, 218732769]} + [23] {[{6/1/2023 12:00:00 AM}, 186324324]} + [24] {[{7/1/2023 12:00:00 AM}, 151700649]} + [25] {[{8/1/2023 12:00:00 AM}, 142653374]} + [26] {[{9/1/2023 12:00:00 AM}, 75763156]}
问题原因
这是因为EF Core的GroupBy与LINQ to Objects的GroupBy行为存在差异:
- 直接在EF中执行GroupBy时,查询会被翻译成SQL在数据库层面执行。原查询的Select阶段已经对每条Truck计算了包含关联集合Sum的Quantity,但EF在翻译GroupBy+Sum的组合逻辑时,错误地将整个数据集的Quantity做了全局汇总,而非按Year和Month分组聚合。
- 先通过ToList加载到内存后,使用的是LINQ to Objects的GroupBy,它会严格按照内存中的数据分组,再对每组的Quantity求和,因此能得到正确结果。
正确写法
通过指定GroupBy的第二个参数(元素选择器),明确告诉EF分组后要聚合的是Quantity字段,即可让EF生成正确的分组聚合SQL:
var results = await DbContext.Trucks .Where(t => t.Departure.HasValue) .Select(t => new { t.Departure!.Value.Year, t.Departure!.Value.Month, Quantity = t.InboundQuantity + t.ToTransfers.Sum(x => x.Quantity) - t.FromTransfers.Sum(x => x.Quantity) }) .GroupBy(t => new { t.Year, t.Month }, x => x.Quantity) .ToDictionaryAsync(g => new DateTime(g.Key.Year, g.Key.Month, 1), g => g.Sum());
内容的提问来源于stack exchange,提问作者Jonathan Wood
相关产品推荐
相关产品推荐

