首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

ADO.NET Entity Framework 實戰筆記(四):ObjectQuery 核心機制、LINQ 擴充方法與不支援操作對照表

     編輯:關於.NET

ObjectQuery<T> 是 Entity Framework 中管理實體集合、封裝查詢建構與執行邏輯的核心類別。它繼承自 ObjectQuery,內部持有 ObjectContext 參考,支援 ESQL 字串查詢與 LINQ 風格方法建構。本文詳細解析其資料載入方式、Execute/ObjectResult 執行模型、型別轉換 (OfType)、22 個支援的 LINQ 擴充方法、12 個不支援的 LINQ 方法及替代方案,附完整 T-SQL 對照。

一、ObjectQuery<T> 三種資料載入/建構方式

ObjectQuery 繼承結構

ObjectQuery 存取方式

1. 直接存取 EntitySet (最常用)


var q = context.DBItem; // 回傳 ObjectQuery<DBItem>,隱含 SELECT VALUE it FROM ctx.DBItem AS it

2. Context.CreateQuery<T>(esql)


// 純 ESQL 字串
var q1 = context.CreateQuery<DBItem>("DBItem"); // 等同 EntitySet 名稱
var q2 = context.CreateQuery<DBItem>("SELECT VALUE it FROM ctx.DBItem AS it WHERE it.ItemID='a'");

3. new ObjectQuery<T>(esql, context) (手動建構)

依 ESQL 內容自動推斷回傳型別:

  • SELECT VALUE it ... → ObjectQuery<Entity>
  • SELECT prop1, prop2 ... → ObjectQuery<DbDataRecord>
  • SELECT VALUE COUNT(...) ... → ObjectQuery<Int32> 等標量型別

// ObjectQuery<DbDataRecord>
var q = new ObjectQuery<DbDataRecord>("SELECT it.ItemID, it.ItemMatter FROM ctx.DBItem AS it", context);

// ObjectQuery<Int32> (標量)
var q = new ObjectQuery<int>("SELECT VALUE COUNT(it.ItemID) FROM ctx.DBItem AS it", context);

二、Execute 方法與 ObjectResult<T> 執行模型

ObjectQuery<T>.Execute(MergeOption) 立即執行查詢,回傳 ObjectResult<T> (實作 IEnumerable<T>、IDisposable):


var q = context.CreateQuery<DBItem>("DBItem");
var result = q.Execute(MergeOption.NoTracking); // 不追蹤,唯讀最佳效能
foreach (var r in result) Console.WriteLine($"{r.ItemID}, {r.ItemMatter}");
result.Dispose(); // 建議使用 using 區塊

ObjectResult 結構

三、型別轉換:OfType<TResult>()

針對繼承體系,篩選特定衍生型別實體:


// 假設 DBItemEx 繼承 DBItem
var q = context.DBItem.OfType<DBItemEx>(); // 回傳 ObjectQuery<DBItemEx>

OfType 示意圖

四、支援的 22 個 LINQ 擴充方法 (含 T-SQL 對照)

方法用途關鍵 T-SQL 特徵
All所有元素滿足條件CASE WHEN NOT EXISTS ... THEN 1 ELSE 0 END
Any任一元素滿足條件CASE WHEN EXISTS ... THEN 1 ELSE 0 END
Take(n)前 N 筆SELECT TOP (n) ...
Skip(n)跳過前 N 筆ROW_NUMBER() OVER (ORDER BY ...) WHERE rn > n (需先 OrderBy)
First / FirstOrDefault第一筆 / 或預設值SELECT TOP (1) ... WHERE ...
WhereLambda 篩選標準 WHERE 子句
Distinct去重SELECT DISTINCT ...
OrderBy / OrderByDescending主排序ORDER BY ... ASC/DESC
ThenBy / ThenByDescending次排序ORDER BY ..., ... ASC/DESC
Average / Sum平均/總和AVG/SUM ... LEFT OUTER JOIN (SELECT ...) ON 1=1
Max / Min最大/最小值MAX/MIN ... LEFT OUTER JOIN ... ON 1=1
Count / LongCount筆數COUNT ... LEFT OUTER JOIN ... ON 1=1
Concat聯集 (不去重)UNION ALL (可跨 Context)
Union聯集 (去重)UNION ALL + DISTINCT (需同 Context)
UnionAll聯集 (保留重複)UNION ALL (需同 Context)
Except差集EXCEPT (需同 Context)
Intersect交集INTERSECT (需同 Context)
Select投影SELECT ... 新欄位 ...
GroupBy分組回傳 IEnumerable<IGrouping<K,E>>,客戶端分組
Join內連接INNER JOIN ... ON ...
GroupJoin群組連接 (左外連接+分組)LEFT OUTER JOIN + 子查詢 COUNT

關鍵範例精選

Join 內連接


var q = context.DBItem.Join(context.DBItemList,
    d => d.ItemID, l => l.ItemID,
    (d, l) => new { d.ItemID, d.ItemMatter, l.ItemValue, l.AutoId, l.NameID });
// T-SQL: INNER JOIN [dbo].[DBItemList] ON [DBItem].[ItemID] = [DBItemList].[ItemID]

GroupJoin 左外連接 + 分組計數


var q = context.DBItem.GroupJoin(context.DBItemList,
    d => d.ItemID, l => l.ItemID,
    (d, l) => new { d.ItemID, d.ItemMatter, Count = l.Count() });
// T-SQL: LEFT OUTER JOIN 子查詢 COUNT(...)

GroupJoin 結果

GroupBy 客戶端分組 (注意:非資料庫端 GROUP BY)


var groups = context.DBItemList.GroupBy(p => p.ItemID); // IEnumerable<IGrouping<string, DBItemList>>
foreach (var g in groups)
{
    Console.WriteLine(g.Key); // ItemID
    foreach (var r in g) Console.WriteLine(r.ItemValue);
}
// T-SQL: 先查出所有資料,再由客戶端記憶體分組 (含 DISTINCT + LEFT JOIN)

GroupBy 類別結構

GroupBy 執行結果

五、不支援的 12 個 LINQ 方法與替代方案

以下方法在 ObjectQuery<T> 上呼叫會拋出 NotSupportedException,需先 .ToList() 取回記憶體再操作:

方法原因替代方案
Aggregate複雜彙總邏輯無法轉譯.ToList().Aggregate(...)
TakeWhile / SkipWhile依賴序列順序的流式處理.ToList().TakeWhile(...)
Reverse需完整序列反轉.AsEnumerable().Reverse()
Last / LastOrDefault需知道總筆數/最後一筆.ToList().Last() 或 OrderByDescending().First()
Single / SingleOrDefault需驗證唯一性 (掃描全表).ToList().Single()
Contains記憶體集合包含判斷.ToList().Contains(...) 或 SQL IN / EXISTS
Distinct (自訂比較器)無法轉譯 IEqualityComparer.ToList().Distinct(comparer)
ElementAt / ElementAtOrDefault索引存取需序列具體化.ToList()[index]
DefaultIfEmpty左外連接語意使用 GroupJoin 或手工 ESQL
SelectMany展平巢狀集合.ToList().SelectMany(...)
SequenceEqual序列逐項比較.ToList().SequenceEqual(...)

替代方案實例


// Aggregate: 記憶體中計算總和
var list = context.DBItemList.ToList();
var sum = list.Aggregate((x, y) => new DBItemList { ItemValue = x.ItemValue + y.ItemValue });
Console.WriteLine(sum.ItemValue); // 54

// TakeWhile / SkipWhile: 記憶體流式處理
var v1 = list.TakeWhile(p => p.ItemValue >= 2); // 條件首次失效停止
var v2 = list.SkipWhile(p => p.ItemValue >= 2); // 條件首次失效後全取

// SelectMany: 展平字串分割
var ls = list.SelectMany(p => p.NameID.Split('0')); // n,1,n,1,n,2...

// SequenceEqual: 比較兩序列內容順序
var l1 = context.DBItemList.Where(p => p.ItemID == "a").ToList();
var l2 = context.DBItemList.Where(p => p.ItemID == "a").ToList();
bool eq = l1.SequenceEqual(l2); // true

SelectMany 對比 Select

總結

ObjectQuery<T> 是 EF 查詢管線的核心:

  1. 支援 ESQL 字串 與 LINQ 方法鏈 兩種建構方式,最終統一轉譯為 T-SQL。
  2. Execute() 提供立即執行與 MergeOption 控制;預設延遲執行 (迭代時)。
  3. 22 個支援的 LINQ 方法 涵蓋絕大多數查詢需求;12 個不支援方法 皆屬記憶體端操作,採 .ToList() 落地後處理即可。
  4. GroupBy、Join、GroupJoin 等複雜操作會產生較大的 T-SQL,需關注效能。
  5. 善用 OfType 處理繼承體系,Include 控制急載入,MergeOption.NoTracking 優化唯讀查詢。

熟練掌握 ObjectQuery 的行為邊界,是寫出高效、正確 EF 程式碼的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved