为了账号安全,请及时绑定邮箱和手机立即绑定

不支持客户端 GroupBy

不支持客户端 GroupBy

PHP
慕婉清6462132 2024-01-20 15:41:03
我有以下 Entity Framework Core 3.0 查询: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));我收到以下错误:Client side GroupBy is not supported.要在客户端或客户端的一部分上运行查询,我将执行以下操作: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));现在可以了。如果我没有在客户端上运行查询,为什么会收到此错误?
查看完整描述

6 回答

?
慕侠2389804

TA贡献1719条经验 获得超6个赞

GroupBy对于 LINQ 的作用和 SQL 的功能GROUP BY似乎存在一个常见的误解。由于我陷入了完全相同的陷阱并且最近不得不思考这个问题,因此我决定对这个问题写一个更彻底的解释。


简短回答:

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

这就是 EF 必须在内存中执行 LINQ 类型的原因GroupBy

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

现在需要显式调用类似.AsEnumerable()or 的函数.ToList(),该函数下载结果集并继续内存中的 LINQ 操作。


长答案:

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

+-----------+------------+

| StudentId | ExerciseId |

+-----------+------------+

|         1 |          1 |

|         1 |          2 |

|         2 |          2 |

|         3 |          1 |

|         3 |          2 |

|         3 |          3 |

+-----------+------------+

X | Y该表中的一条记录表示该学生X已解决习题Y

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

在我们的示例中,我们可能想要获得一个Dictionary<int, List<int>>,其中包含每个学生已解决的练习的列表。使用 LINQ,这非常简单:

var result = solvedExercises
    .GroupBy(e => e.StudentId)
    .ToDictionary(e => e.Key, e => e.Select(e2 => e2.ExerciseId).ToList());

输出(完整代码请参见dotnetfiddle):

Student #1: 1 2 
Student #2: 2 
Student #3: 1 2 3

这很容易用 C# 数据类型来表示,因为我们可以嵌套List,并且可以嵌套Dictionary到任意深度。

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

  • 生成多个结果表,

  • 将分组的行放入数组中或

  • 以某种方式插入“结果集分隔符”。

据我所知,这些方法都没有在实践中得到实施。最多,有一些像 MySQL 那样的 hacky 解决方法GROUP_CONCAT,它允许将结果行组合成一个字符串。

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

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

SELECT StudentId,COUNT(ExerciseId)
FROM solvedExercises
GROUP BY StudentId

...这将产生

+-----------+-------------------+
| StudentId | COUNT(ExerciseId) |
+-----------+-------------------+
|         1 |                 2 |
|         2 |                 1 |
|         3 |                 3 |
+-----------+-------------------+

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

由 EF Core 实现的:执行

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 查询。


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

在 EF Core 3.0 中,开发人员选择在这种情况下抛出异常,而不是隐式执行此操作;这可以防止意外下载包含数百万行的整个、可能很大的表,而由于测试数据库较小,这在开发过程中可能会被忽视。


查看完整回答
反对 回复 2024-01-20
?
翻翻过去那场雪

TA贡献2065条经验 获得超13个赞

您的.GroupBy(y => y.LanguageCode).ToDictionaryAsync(y => y.Key, y => y.Select(z => z.Name));无法转换为 SQL。EF Core 3.0 将抛出异常,以确保您知道Units在分组之前将从数据库中获取所有记录并将其映射到字典。

这是 EF Core 3.0 中最重大的重大变化。


查看完整回答
反对 回复 2024-01-20
?
杨魅力

TA贡献1811条经验 获得超6个赞

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

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);


查看完整回答
反对 回复 2024-01-20
?
德玛西亚99

TA贡献1770条经验 获得超3个赞

支持客户端分组依据

使用 EF Core 3.1.15.0 进行测试

以下代码返回Client side GroupBy is not supported.错误:

MyEntity
    .GroupBy(x => x.MyProperty)
    .ToDictionaryAsync(x => x.Key, x => x.Count())
    .Dump();

.Select()但由于某种原因,您可以在 后面添加.GroupBy(),它会编译并运行预期的 SQL:

MyEntity
    .GroupBy(x => x.MyProperty)
    .Select(g => new { Key = g.Key, Count = g.Count() })
    .ToDictionaryAsync(x => x.Key, x => x.Count)
    .Dump();

编译为:

SELECT [t].[MyProperty] AS [Key], COUNT(*) AS [Count]
FROM [dbo].[MyEntity] AS [t]
GROUP BY [t].[MyProperty]
查看完整回答
反对 回复 2024-01-20
?
繁星淼淼

TA贡献1775条经验 获得超11个赞

var test = unitOfWork.PostCategory.GetAll().Include(u=>u.category).GroupBy(g => g.category.name).Select(s => new

                {


                    name = s.Key,

                    count = s.Count()


                }).OrderBy(o=>o.count).ToList();

你可以尝试这个代码部分...它会起作用..我已经尝试过了


查看完整回答
反对 回复 2024-01-20
?
牛魔王的故事

TA贡献1830条经验 获得超3个赞

linq GroupBy 方法可以完成数据库查询无法完成的任务。这就是 linq 抛出异常的原因。这并不是一个缺失的功能,但在旧版本的 linq 中,它只是枚举整个表,然后在本地运行 GroupBy。


Linq 查询语法恰好有一个group可以转换为数据库查询的关键字。


以下是如何使用查询语法在数据库上运行查询的主要工作示例:


var kvPairs = from y in context.Units

              from u in y.UnitsI18N

              orderby u.Name

              group u by u.LanguageCode into g

              select new KeyValuePair<string,IEnumerable<string>>(g.Key, g.Select(z => z.Name));


return new Dictionary<string,IEnumerable<string>>>(kvPairs);

有关详细信息,请参阅 Microsoft 的这篇文章:https://learn.microsoft.com/en-us/ef/core/querying/complex-query-operators#groupby


查看完整回答
反对 回复 2024-01-20
  • 6 回答
  • 0 关注
  • 122 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信