实体框架v6 GroupBy丢失原始订购

时间:2015-11-24 07:32:39

标签: entity-framework linq-to-entities 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);

它正在使用具有Vehicle对象和Rental对象的对象。大约有12K辆车,分成大约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);

任何帮助或指示将不胜感激。我无法确定为什么原始查询不能保持一切顺序。

1 个答案:

答案 0 :(得分:3)

  

我希望它可以通过RangeId订购车辆,然后租借

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

LINQ的GroupBy似乎与SQL的GROUP BY相似,但实际上却完全不同。

SQL中的

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

SELECT Brand, COUNT(*) 
FROM Cars
GROUP BY Brand

...你只看到Brand及其数量。你没有在小组中看到汽车。

这正是LINQ的GroupBy所做的:它产生了完整的对象组。原始数据中的所有信息仍然存在。你会看到按品牌分组的汽车。

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

EF以不同方式实施GroupBy。它在一个查询中获取所有数据,然后在内存中构建组。您不会在生成的SQL中看到GROUP BY。您会看到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);

但它也可能是一个索引问题。如果Rate.Rental上没有正确的索引,那么按该列排序是很昂贵的。