6 回答

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 中,開發人員選擇在這種情況下拋出異常,而不是隱式執行此操作;這可以防止意外下載包含數百萬行的整個、可能很大的表,而由于測試數據庫較小,這在開發過程中可能會被忽視。

TA貢獻2065條經驗 獲得超14個贊
您的.GroupBy(y => y.LanguageCode).ToDictionaryAsync(y => y.Key, y => y.Select(z => z.Name));
無法轉換為 SQL。EF Core 3.0 將拋出異常,以確保您知道Units
在分組之前將從數據庫中獲取所有記錄并將其映射到字典。
這是 EF Core 3.0 中最重大的重大變化。

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

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]

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();
你可以嘗試這個代碼部分...它會起作用..我已經嘗試過了

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
- 6 回答
- 0 關注
- 274 瀏覽
添加回答
舉報