我有一個包含一對多資料集合的資料庫表。從該表中,我需要制作一個代表資料的字典。鍵將是主 ID,值將是與該主 ID 關聯的 ID。我的資料集如下

并且預期的字典應該如下所示

我嘗試了以下代碼,但沒有按預期作業。它回傳 Dictionary<key,groupbydata 集合>,而不是 Dictionary<int,List>。
Table.GroupBy(item => item.MasterId).ToDictionary(item => item.Key);
有什么想法嗎?
uj5u.com熱心網友回復:
我嘗試了以下代碼,但沒有按預期作業。它回傳 Dictionary<key,groupbydata 集合>,而不是 Dictionary<int,List>。
這里對 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 具有鍵,它里面有專案,就像一個陣列有一個長度,里面有專案。我們可以把它變成字典,但首先需要做更多的作業。
GroupBy 將整個事物項作為值輸出的事實是一個問題,因為您不想要 Dictionary<int, List<Thing>>
GroupBy 有另一種形式,您可以在其中提供第二個引數以從事物中獲取值,而不是使用整個事物
Table.GroupBy(t => t.MasterId, t => t.AssociatedId);
這一次,只有 AssociatedId 被用作 IGrouping 中專案的值。在 json 風格中,它看起來像:
[
{
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 has another form, which takes some code that generates the value as well as the key. You can use this form to turn the IGrouping into a List. Remember that you already have an IGrouping that is full of ints so it's a simple case of
GroupBy(...).ToDictionary(g => g.Key, g => g.ToList());
Giving you an entire expression of:
Table
.GroupBy(t => t.MasterId, t => t.AssociatedId)
.ToDictionary(g => g.Key, g => g.ToList());
There are, of course, other ways to write this.. You could leave the GroupBy producing IGrouping<Thing> and instead Select the Associated ID out during the ToDictionary
.GroupBy(t => t.MasterId)
.ToDictionary(g => g.Key, g => g.Select(t => t.AssociatedId).ToList())
You could use the form of groupby that runs over the grouped result doing something else:
.GroupBy(t => t.MasterId, t => t.AssociatedId, (k, g) => new { K= k, L = g.ToList() } )
.ToDictionary(g => g.K, g => g.L)
There is always going to be tens of ways to skin this cat.. Most important for you is to appreciate that GroupBy turns a 1D list into a 2D list, which leads me to the footnote...
Footnote about databases
This is what Panagiotis is getting at. LINQ GroupBy is very different to SQL GROUP BY.
In an SQL GROUP BY you choose what things are going to be your key, and your only other option for getting any data out of it that is not key, is to perform an aggregation right there and then, which throws data away
SELECT MasterId, MIN(AssociatedId), MAX(AssociatedId)
FROM Table
GROUP BY MasterId
You simply cannot have the Key, and then all the associated data with SQL GROUP BY. All your rows with the same MasterId are thrown into a bucket with that masterid label on the outside, mixed up and you can only pull data out of the bucket using an aggregate operation like "the maximum AssociatedId, the average (nonsensical as it is) AssociatedId" etc. This mixes your data up because the MAX(AssociatedID) comes from one row, the MAX(OrderItemCount) comes from another row..
LINQ GroupBy clumps together the rows under the common key but then hands you back the set of buckets with all the data still inside, as whole rows of still-in-one-piece data. You can GroupBy in LINQ and then ask for the First() in each group and you get eg 584753,5 -> SQL just doesn't have that concept at all. There is no "first" anything after it's been thrown into a grouping bucket
..這意味著您在此處表達的 LINQ 根本無法轉換為 SQL 并在服務器上執行。如果您嘗試(在 EFCore 上),您將收到錯誤訊息“此查詢必須在客戶端完成” - 在某些舊版本的 EF(核心和非核心)中,“我只需將所有行拉到客戶端并在那里做”是自動的,我們已經擺脫了這一點,因為自動下載一百萬行只是為了找到資料庫無法做的事情是開發人員應該具體做出的決定
uj5u.com熱心網友回復:
您需要將分組內的值轉換為字典
var test = Table.GroupBy(item => item.MasterId).ToDictionary(g => g.Key, v=> v.Select(x => x.InternalValue));
所以你有一個 Dictionary<MasterID, IEnumerable >
uj5u.com熱心網友回復:
問題不明確,包含矛盾。EF 沒有資料集或資料表——它甚至沒有表。DbSet 不是表的表示形式,它是將表資料映射到客戶端物件的一種方式。
EF 查詢轉換為 SQL。GROUP BY 不嵌套值,它聚合它們,每組只回傳一個值。這和你問的相反。SQL 結果總是平坦的。
另一方面,可以將 LINQ 與記憶體中的ADO.NET DataTable 物件一起使用,就像與任何其他容器一起使用一樣。但是,DataRow 沒有命名列,因此使用LINQ to Dataset函式來檢索特定欄位。
除非您使用 Visual Studio 的設計器創建了 DataTable,該設計器會生成自定義 DataRow 派生類,這些類將列值作為屬性公開。
物體框架
如果您使用 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);
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/411348.html
標籤:
上一篇:C#-List<Items>中的專案與List<ItemsContainer>中的ItemsContainer之間的匹配
