Entity Framework v6 GroupBy 丢失原始排序

lee*_*n3o 3 linq-to-entities entity-framework entity-framework-6

我有一个查询的以下部分(这是一个更大的查询的结尾 - queryBuilder 是一个 IQueryable

            var results = queryBuilder
                          .OrderBy(x => x.Vehicle.RangeId)
                          .ThenBy(x => x.Rate.Rental)
                          .GroupBy(x => x.Vehicle.RangeId)
                          .Select(x => x.FirstOrDefault())
                          .OrderBy(x => x.Rate.Rental);
Run Code Online (Sandbox Code Playgroud)

它使用带有 Vehicle 对象和 Rental 对象的对象。大约有 12,000 辆车,分为大约 40 个范围(RangeId - Indexed int 指示)。

上面的查询工作正常,但是不符合我的预期。我希望它先按 RangeId 订购车辆,然后按租金(最低租金优先)。

然后按 RangeId 将它们分组并从组中选择第一个,这“应该”是最便宜的租金,因为它在调用 groupby 之前已被订购。

然而。它不是。它只是无序随机获得一个。有时它是第二便宜的。有时是第 5 次,依此类推。出于某种原因,GroupBy 不遵守原始订单!

通过执行以下操作,我可以使其正常工作。然而,表演绝对是可怕的,需要很长时间才能完成。

            var results = queryBuilder
                          .OrderBy(x => x.Vehicle.RangeId)
                          .ThenBy(x => x.Rate.Rental)
                          .GroupBy(x => x.Vehicle.RangeId)
                          .Select(x => x.OrderBy(o => o.Rate.Rental).FirstOrDefault())
                          .OrderBy(x => x.Rate.Rental);
Run Code Online (Sandbox Code Playgroud)

任何帮助或指示将不胜感激。我无法弄清楚为什么原始查询没有使一切井然有序。

Ger*_*old 5

我希望它先按 RangeId 订购车辆,然后再按租金订购

在 LINQ to Entities 查询中,GroupBy将简单地忽略a 之前的任何排序。您甚至不会在执行的 SQL 中看到它。那是因为实体框架将分组表达式按顺序排序(在您的情况下x => x.Vehicle.RangeId)。这是为什么?

LINQ 的GroupBy看起来和 SQL 很相似GROUP BY,但实际上却大不相同。

GROUP BY在 SQL 中是“破坏性的”,我的意思是除了列中的列之外的任何信息GROUP BY都丢失了(聚合表达式除外)。如果你这样做...

SELECT Brand, COUNT(*) 
FROM Cars
GROUP BY Brand
Run Code Online (Sandbox Code Playgroud)

...你只看到Brand和他们的计数。您看不到组中的汽车。

这正是 LINQGroupBy所做的:它生成一组完整的对象。原始数据中的所有信息都还在。您会看到按品牌分组的汽车。

这意味着翻译GroupBy为的ORMGROUP BY很难构建结果集。LINQ to SQL 就是这样做的。它首先执行一个GROUP BY查询,然后它需要单独的查询(实际上每组一个)来弥补“丢失”的数据。

EF 的实现方式GroupBy不同。它在一个查询中获取所有数据,然后在内存中构建组。您不会GROUP BY在生成的 SQL 中看到。你会看到一个ORDER BY。我认为 EF 更喜欢排序的 SQL 查询结果,以便在内存中进行更有效的处理。(我可以想象与管道中的其他 LINQ 语句更好地结合)。

这就是为什么之前的任何排序GroupBy都被忽略的原因。以及为什么您只能在分组后应用排序。

表现绝对糟糕

从这里很难说为什么会这样。也许您可以在内存中进行排序:

var results = queryBuilder
              .GroupBy(x => x.Vehicle.RangeId)
              .Select(x => x.OrderBy(o => o.Rate.Rental).FirstOrDefault())
              .Select(o => new { o.Rate.Rental, o }
              .AsEnumerable()
              .OrderBy(x => x.Rental);
Run Code Online (Sandbox Code Playgroud)

但这也可能是索引问题。如果 上没有合适的索引Rate.Rental,则按该列排序的成本很高。