我尝试了以下代码,但没有按预期工作。而不是 Dictionary 它返回 Dictionary。
这里对 LINQ GroupBy 有一个基本的误解
GroupBy 的输出是 IGrouping 的枚举,它类似于列表列表。然而,它并不完全是 List<List<something>>
IGrouping 是具有属性 Key 和相关值集合的东西。无论源集合推动了分组的创建,其中的所有值都是通过源对象上的某些操作以及创建键的某些操作创建的
在最简单的情况下,您使用过的那个,您告诉 GroupBy 如何生成一个键作为纯单个简单值,MasterId,它是 Table 中所有对象的属性。您没有指定自定义操作来从项目生成值,因此将整个项目用作值
对于发布的示例,您的项目是一对整数,也许我们可以将它们建模为:
record Thing(int MasterId, int AssociatedId);
仅按 MasterId 分组:
GroupBy(t => t.MasterId)
意味着您得到的结果类似于(JSON-esque 表示)
[
{
Key: 584753,
this: [ { MasterId: 584753, AssociatedId: 5 },{ MasterId: 584753, AssociatedId: 4 },{ MasterId: 584753, AssociatedId: 3 } ]
},
{
Key: 584754,
this: [ { MasterId: 584754, AssociatedId: 4 },{ MasterId: 584754, AssociatedId: 3 } ]
},
...
我说 json-esque 是因为 json 不能真正代表一个对象数组,该数组也有一个不是对象之一的属性。我能得到的最接近的方法是让您想象一个具有默认属性 this 的对象,即数组
GroupBy 生成的不是Dictionary<int, List<int>>,它是“具有 Key 属性的 IGrouping 对象列表,也是表中的整个项目的 Thing 对象列表,包括主 ID 和关联 ID " - IGrouping 有一个 Key 并且里面有项目,就像一个数组有一个 Length 并且里面有项目一样。我们可以把它变成字典,但首先需要做更多的工作。
GroupBy 将整个 Thing 项作为值输出这一事实是一个问题,因为您不想要 Dictionary<int, List<Thing>>
GroupBy 有另一种形式,您可以在其中提供第二个参数以从事物中获取值,而不是使用整个事物
Table.GroupBy(t => t.MasterId, t => t.AssociatedId);
这一次,您的 IGrouping 中的项目值仅采用 AssociatedId。在 json-esque 中它看起来像:
[
{
Key: 584753,
this: [ 5, 4, 3 ]
},
{
Key: 584754,
this: [ 4, 3 ]
},
...
这更接近你想要的,它不是字典,它是 IGrouping 的列表,而 IGrouping 不是列表
进入ToDictionary的使用
如果使用 ToDictionary 的单参数形式:
GroupBy(...).ToDictionary(g => g.Key)
你将得到一个Dictionary<int, IGrouping> - int 来自作为 int 的 Key,这是在分组操作期间做出的决定。该值是一个 IGrouping,因为这种形式的 ToDictionary 仅使用输入的整个项目作为值。整个项目是一个 IGrouping。
ToDictionary 有另一种形式,它采用一些代码来生成值和键。您可以使用此表单将 IGrouping 转换为列表。请记住,您已经有一个充满整数的 IGrouping,所以这是一个简单的例子
GroupBy(...).ToDictionary(g => g.Key, g => g.ToList());
给你一个完整的表达方式:
Table
.GroupBy(t => t.MasterId, t => t.AssociatedId)
.ToDictionary(g => g.Key, g => g.ToList());
当然,还有其他写法。你可以通过生成IGrouping<Thing> 离开 Group,而是在 ToDictionary 期间选择关联 ID
.GroupBy(t => t.MasterId)
.ToDictionary(g => g.Key, g => g.Select(t => t.AssociatedId).ToList())
您可以使用 groupby 的形式在分组结果上执行其他操作:
.GroupBy(t => t.MasterId, t => t.AssociatedId, (k, g) => new { K= k, L = g.ToList() } )
.ToDictionary(g => g.K, g => g.L)
总有几十种方法可以给这只猫剥皮。对您来说最重要的是要欣赏 GroupBy 将 1D 列表转换为 2D 列表,这将我引向脚注...
关于数据库的脚注
这就是 Panagiotis 的目的。 LINQ GroupBy 与 SQL GROUP BY 非常不同。
在 SQL GROUP BY 中,您选择哪些内容将成为您的关键,而从中获取任何非关键数据的唯一其他选择是立即执行聚合,然后将数据丢弃
SELECT MasterId, MIN(AssociatedId), MAX(AssociatedId)
FROM Table
GROUP BY MasterId
您根本无法拥有 Key,然后是 SQL GROUP BY 的所有关联数据。具有相同 MasterId 的所有行都被放入外部带有该 masterid 标签的存储桶中,混合在一起,您只能使用聚合操作从存储桶中提取数据,例如“最大 AssociatedId,平均值(虽然很荒谬) AssociatedId”等。这会混淆您的数据,因为 MAX(AssociatedID) 来自一行,MAX(OrderItemCount) 来自另一行..
LINQ GroupBy 将公共键下的行聚集在一起,但随后将所有数据仍在其中的存储桶集作为整行仍然一体的数据交还给您。您可以在 LINQ 中进行 GroupBy,然后在每个组中请求 First(),然后您会得到例如 584753,5 -> SQL 根本没有这个概念。扔进分组桶后就没有“第一”的东西了
..这意味着您在此处表达的 LINQ 根本无法转换为 SQL 并在服务器上执行。如果您尝试(在 EFCore 上),您将收到错误消息“此查询必须在客户端完成” - 在某些旧版本的 EF(核心和非核心)中,“我只需将所有行拉到客户端并在那里做”是自动的,我们已经摆脱了这一点,因为自动下载一百万行只是为了找到数据库不能做的事情是开发人员应该具体做出的决定