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


var q = context.DBItem; // 回傳 ObjectQuery<DBItem>,隱含 SELECT VALUE it FROM ctx.DBItem AS it
// 純 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'");
依 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);
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 區塊

針對繼承體系,篩選特定衍生型別實體:
// 假設 DBItemEx 繼承 DBItem
var q = context.DBItem.OfType<DBItemEx>(); // 回傳 ObjectQuery<DBItemEx>

| 方法 | 用途 | 關鍵 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 ... |
Where | Lambda 篩選 | 標準 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 |
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]
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(...)

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)


以下方法在 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

ObjectQuery<T> 是 EF 查詢管線的核心:
Execute() 提供立即執行與 MergeOption 控制;預設延遲執行 (迭代時)。.ToList() 落地後處理即可。GroupBy、Join、GroupJoin 等複雜操作會產生較大的 T-SQL,需關注效能。OfType 處理繼承體系,Include 控制急載入,MergeOption.NoTracking 優化唯讀查詢。熟練掌握 ObjectQuery 的行為邊界,是寫出高效、正確 EF 程式碼的關鍵。