从 linq 结果集 C# 创建一对多集合

Create a one to many collection from linq result setC#

我有一个包含一对多数据集合的数据库table。从那个 table 我需要制作一个代表数据的字典。键将是主 ID,值将是与该主 ID 关联的 ID。我的数据集如下

预期的字典应该如下所示

我尝试了以下代码,但它没有按预期工作。它返回的不是 Dictionary,而是 Dictionary

 Table.GroupBy(item => item.MasterId).ToDictionary(item => item.Key);

有什么想法吗?

您需要将分组内的值转换为字典

var test = Table.GroupBy(item => item.MasterId).ToDictionary(g => g.Key, v=> v.Select(x => x.InternalValue));

所以你得到了一个 Dictionary

题目含糊不清,自相矛盾。 EF 没有数据集或数据tables——它甚至没有tables。 DbSet 不是 table 的表示,它是一种将 table 数据映射到客户端对象的方法。

EF 查询转换为 SQL。 GROUP BY 不嵌套值,它 聚合 它们,每组只返回一个值。这与你问的相反。 SQL 结果总是持平。

另一方面,可以将 LINQ 与 内存中 ADO.NET DataTable 对象一起使用,就像它与任何其他容器一起使用一样。尽管 DataRow 没有命名列,因此 LINQ to Dataset 函数用于检索特定字段。

除非您使用 Visual Studio 的设计器创建了 DataTable,它会生成自定义的 DataRow 派生 类,将列值公开为属性。

Entity Framework

如果使用 EF,则必须将平面数据加载到内存中,然后再嵌套。你应该避免使用GroupBy因为你真的不想生成GROUP BY.

字典的每个键不能有很多值。您可以使用 ToLookup 来生成一个 ILookup 对象。

以下查询将嵌套整行:

var lookup= dbContext.OrderItems
                     .Where(...)
                     .ToLookup(o=>o.OrderID);

虽然这只会加载所需的列

var lookup= dbContext.OrderItems
                     .Where(...)
                     .Select(o=>new {o.OrderItemId,o.OrderId})
                     .ToLookup(o=>o.OrderID);

I tried the following code but it didnt work as expected. Instead of Dictionary<int,List> it returned Dictionary<key,groupbydata collection>.

这里对 LINQ GroupBy 有一个基本的误解

GroupBy 的输出是 IGrouping 的枚举,它类似于列表的列表。但是,它不完全是 List<List<something>>

IGrouping 是具有 属性 键和关联的值集合的东西。无论源集合驱动分组的创建,其中的所有值都是通过对源对象的某些操作以及创建键的某些操作创建的

在最简单的情况下,您使用过的那个,您告诉 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 对象列表,也是 table, master id 和 associated id" - IGrouping 有一个 Key 并且它里面有项目,就像一个数组有一个 Length 并且里面有项目一样。我们可以将它变成一个字典,但首先需要做更多的工作。

GroupBy 将整个 Thing 项作为值输出这一事实是一个问题,因为您不想要 Dictionary<int, List<Thing>>

GroupBy 有另一种形式,您可以在其中提供第二个参数以从事物派生值,而不是使用整个事物

Table.GroupBy(t => t.MasterId, t => t.AssociatedId);

这次仅将 AssociatedId 用作 IGrouping 中项目的值。在 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 来自 Key 是一个 int,这是在分组操作期间做出的决定。该值是一个 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());

当然,还有其他的写法。您可以让 GroupBy 产生 IGrouping<Thing> 而不是 Select 在 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 将一维列表转换为二维列表,这使我想到了脚注...

关于数据库的脚注

这就是 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(核心和非核心)中“我只会将所有行拉到客户端并在那里做”是自动的,我们已经放弃了,因为自动下载一百万行只是为了找到数据库不能做的事情是开发人员应该具体做出的决定