首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >不支持客户端GroupBy

不支持客户端GroupBy
EN

Stack Overflow用户
提问于 2019-09-27 16:43:53
回答 6查看 48.9K关注 0票数 95

我有以下实体框架核心3.0查询:

代码语言:javascript
运行
复制
var units = await context.Units
  .SelectMany(y => y.UnitsI18N)
  .OrderBy(y => y.Name)
  .GroupBy(y => y.LanguageCode)
  .ToDictionaryAsync(y => y.Key, y => y.Select(z => z.Name));

我得到以下错误:

代码语言:javascript
运行
复制
Client side GroupBy is not supported.

要在客户机上或其中的一部分上运行查询,我将执行以下操作:

代码语言:javascript
运行
复制
var units = context.Units
  .SelectMany(y => y.UnitsI18N)
  .OrderBy(y => y.Name)
  .AsEnumerable()
  .GroupBy(y => y.LanguageCode)
  .ToDictionary(y => y.Key, y => y.Select(z => z.Name));

现在起作用了。

如果我没有在客户端上运行查询,为什么会得到这个错误?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2019-10-08 05:34:10

您的.GroupBy(y => y.LanguageCode).ToDictionaryAsync(y => y.Key, y => y.Select(z => z.Name));不能转换为SQL。EFCore3.0将抛出异常,以确保您知道Units中的所有记录都将在分组和映射到字典之前从数据库中提取。

这是EF核心3.0的最高突破变化。https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes

票数 43
EN

Stack Overflow用户

发布于 2020-03-20 17:13:04

对于LINQ GroupBy做什么和SQL GROUP BY能够做什么,似乎有一种常见的误解。因为我掉进了同样的陷阱,最近不得不把我的头绕到这件事上,所以我决定对这个问题写一个更彻底的解释。

简短回答:

LINQ GroupBy,与SQL GROUP BY语句有很大不同: LINQ just 根据键将底层集合划分为块,而SQL还应用聚合函数将每个块压缩为单个值。

这就是为什么EF必须在内存中执行LINQ类GroupBy的原因。

在EFCore3.0之前,这是隐式的,因此EF下载了所有结果行,然后应用了LINQ GroupBy。但是,这种隐式行为可能使程序员期望整个LINQ查询在SQL中执行,当结果集相当大时,可能会对性能产生巨大影响。因此,GroupBy的隐式客户端评估是在EF Core 3.0中完全禁用

现在需要显式调用函数,如.AsEnumerable().ToList(),它们下载结果集并继续在内存中执行LINQ操作。

长答案:

下表solvedExercises将是这个答案的运行示例:

代码语言:javascript
运行
复制
+-----------+------------+
| StudentId | ExerciseId |
+-----------+------------+
|         1 |          1 |
|         1 |          2 |
|         2 |          2 |
|         3 |          1 |
|         3 |          2 |
|         3 |          3 |
+-----------+------------+

本表中的记录X | Y表示学生X已经解决了练习Y

在这个问题中,描述了LINQ的GroupBy方法的一个常见用例:获取一个集合并将其分组为块,其中每个块中的行共享一个公共键。

在我们的示例中,我们可能希望得到一个Dictionary<int, List<int>>,它包含了每个学生解决的练习列表。对于LINQ,这是非常简单的:

代码语言:javascript
运行
复制
var result = solvedExercises
    .GroupBy(e => e.StudentId)
    .ToDictionary(e => e.Key, e => e.Select(e2 => e2.ExerciseId).ToList());

输出(完整代码请参见多管乐器):

代码语言:javascript
运行
复制
Student #1: 1 2 
Student #2: 2 
Student #3: 1 2 3 

这很容易用C#数据类型来表示,因为我们可以尽可能深入地嵌套ListDictionary

现在,我们尝试将其想象为SQL查询结果。SQL查询结果通常表示为一个表,在该表中我们可以自由选择返回的列。要将上面的查询表示为SQL查询结果,我们需要

  • 生成多个结果表,
  • 将分组行放入数组或
  • 以某种方式插入“结果集分隔符”。

据我所知,这些方法都没有在实践中得到实施。最多有一些类似MySQL的GROUP_CONCAT之类的讨厌的工作,它允许将结果行组合成一个字符串(相关的答案)。

因此,我们看到,SQL 不能产生与LINQ的GroupBy概念相匹配的结果。

相反,SQL只允许所谓的聚合:例如,如果我们想计算学生通过了多少练习,我们就会写。

代码语言:javascript
运行
复制
SELECT StudentId,COUNT(ExerciseId)
FROM solvedExercises
GROUP BY StudentId

...which将产生

代码语言:javascript
运行
复制
+-----------+-------------------+
| StudentId | COUNT(ExerciseId) |
+-----------+-------------------+
|         1 |                 2 |
|         2 |                 1 |
|         3 |                 3 |
+-----------+-------------------+

聚合函数将一组行缩减为单个值,通常是标量。例如行计数、和、最大值、最小值和平均值。

这是由EF Core实现的:执行

代码语言:javascript
运行
复制
var result = solvedExercises
    .GroupBy(e => e.StudentId)
    .Select(e => new { e.Key, Count = e.Count() })
    .ToDictionary(e => e.Key, e => e.Count);

生成上述SQL。注意Select,它告诉EF在生成的SQL查询中应该使用哪个聚合函数。

总之,LINQ函数比SQL GroupBy语句更为通用,由于GROUP BY的限制,该语句只允许返回一个单一的二维结果表。因此,在下载SQL结果集之后,必须在内存中计算类似问题中的查询和这个答案中的第一个示例的查询。

在EFCore3.0中,开发人员在这种情况下,选择抛出一个异常没有隐式地执行此操作,而是防止意外下载包含数百万行的整个、可能较大的表,这在开发过程中可能会因为一个小的测试数据库而被忽略。

票数 214
EN

Stack Overflow用户

发布于 2019-10-24 07:51:22

一个可能的解决方案(对我有用)是在一个GroupBy对象上创建List

代码语言:javascript
运行
复制
var units = (
  await context.Units
  .SelectMany(y => y.UnitsI18N)
  .GroupBy(y => y.LanguageCode)
  .ToDictionaryAsync(y => y.Key, y => y.Select(z => z.Name))
  ).ToList().OrderBy(y => y.Name);
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58138556

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档