Entity Framework 除支援 LINQ to Entities 外,還提供功能強大的 Entity SQL (ESQL)——一種以字串為基礎、語法類 T-SQL 但面向概念模型 (EDM) 的查詢語言。本文系統整理 ESQL 的查詢結果型別、核心關鍵字、參數化查詢、中文欄位處理、CommandText/ToTraceString 除錯、ObjectQuery LINQ 風格擴充方法、註解/成員存取/分行、運算子、分頁與集合運算,並附帶對應生成的 T-SQL,助你快速上手並深入理解轉譯機制。
| 查詢形式 | 關鍵字 | 回傳型別 | 適用場景 |
|---|---|---|---|
| 實體查詢 | SELECT VALUE entity | ObjectQuery<Entity> | 需完整實體物件、後續修改/追蹤 |
| 投影/標量查詢 | SELECT prop1, prop2 | ObjectQuery<DbDataRecord> | 僅需部分欄位、匿名記錄、彙總函數 |
| 單一值/標量 | SELECT VALUE scalar | ObjectQuery<PrimitiveType> | COUNT、MAX、單一欄位值集合 |
// 1. ObjectQuery<Entity>
string esql = "SELECT VALUE it FROM ctx.DBItemList AS it";
var q1 = ctx.CreateQuery<DBItemList>(esql);
foreach (var r in q1) Console.WriteLine(r.NameID);
// 2. ObjectQuery<DbDataRecord>
esql = "SELECT it.NameID FROM ctx.DBItemList AS it";
var q2 = ctx.CreateQuery<DbDataRecord>(esql);
foreach (var r in q2) Console.WriteLine(r["NameID"]);
// 3. ObjectQuery<Int32> (標量)
esql = "SELECT VALUE COUNT(it.NameID) FROM ctx.DBItemList AS it";
var q3 = ctx.CreateQuery<int>(esql);
foreach (var n in q3) Console.WriteLine(n);
it 關鍵字 (範圍變數)預設別名為 it,可透過 ObjectQuery.Name 屬性自訂:
ctx.DBItemList.Name = "wxd";
var q = ctx.DBItemList.Where("wxd.ItemValue = 5"); // 使用自訂別名
VALUE 關鍵字強制回傳單一值/實體,而非記錄結構:
// 回傳標量
esql = "SELECT VALUE AVG(it.ItemValue) FROM ctx.DBItemList AS it";
// 回傳實體
esql = "SELECT VALUE it FROM ctx.DBItemList AS it";
// 回傳建構的 ROW (仍為 DbDataRecord)
esql = "SELECT VALUE ROW(it.ItemValue, it.NameID, 'wxd' AS wxwinter) FROM ctx.DBItemList AS it";
string esql = "SELECT VALUE it FROM ctx.DBItemList AS it WHERE it.ItemValue = @v1 OR it.NameID = @v2";
var p1 = new ObjectParameter("v1", 3);
var p2 = new ObjectParameter("v2", "n01");
var q = ctx.CreateQuery<DBItemList>(esql, p1, p2);
使用 [] 括住識別碼:
// 欄位名為「值」
var q = ctx.typeTest.Where("it.[值] == 22.22"); // 正確
// var q = ctx.typeTest.Where("it.值 == 22.22"); // 錯誤

string esql = "SELECT VALUE it FROM ctx.DBItemList AS it";
var q = ctx.CreateQuery<DBItemList>(esql);
Console.WriteLine("【ESQL】" + q.CommandText); // 原始 ESQL 字串
Console.WriteLine("【T-SQL】" + q.ToTraceString()); // 轉譯後的 T-SQL (需先 Open Connection)
輸出範例:
【ESQL】SELECT VALUE it FROM ctx.DBItemList AS it
【T-SQL】SELECT [Extent1].[AutoId] AS [AutoId], [Extent1].[NameID] AS [NameID], [Extent1].[ItemID] AS [ItemID], [Extent1].[ItemValue] AS [ItemValue] FROM [dbo].[DBItemList] AS [Extent1]
ObjectQuery 提供一組接受 ESQL 字串片段的擴充方法,支援鏈式呼叫:
| 方法 | 簽名 | 說明 | 範例 |
|---|---|---|---|
Where | Where(predicate, params ObjectParameter[]) | 篩選條件 | q.Where("it.ItemValue = 5 AND it.NameID = 'n01'") |
OrderBy | OrderBy(keys, params ObjectParameter[]) | 排序 | q.OrderBy("it.ItemValue, it.ItemID DESC") |
Select | Select(projection, params ObjectParameter[]) | 投影 (回傳 DbDataRecord) | q.Select("it.ItemValue AS a, it.NameID") |
SelectValue | SelectValue<T>(projection, ...) | 投影單一值 (回傳 ObjectQuery<T>) | q.SelectValue<int>("it.ItemValue + it.AutoId") |
Top | Top(count, ...) | 取前 N 筆 | q.Top("3") |
Skip | Skip(keys, count, ...) | 跳過前 N 筆 (需先 OrderBy) | q.OrderBy("it.ItemValue").Skip("it.ItemValue", "5") |
GroupBy | GroupBy(keys, projection, ...) | 分組彙總 | q.GroupBy("it.ItemID", "it.ItemID, SUM(it.ItemValue) AS ValueSum") |
Include | Include(path) | 急載入關聯導航屬性 | ctx.DBItem.Include("DBItemList") |
// 跳過前 5 筆,取 3 筆 (第 2 頁,頁面大小 3)
var q = ctx.DBItemList.OrderBy("it.ItemValue").Skip("it.ItemValue", "5").Top("3");
// 產生 T-SQL: SELECT TOP (3) ... FROM (SELECT ..., row_number() OVER (ORDER BY ...) AS rn ... ) WHERE rn > 5
// 方式一:Include (單次查詢 JOIN 載入)
var q = ctx.DBItem.Include("DBItemList");
foreach (var item in q)
foreach (var child in item.DBItemList) Console.WriteLine(child.NameID);
// 方式二:显式 Load (兩次查詢)
var q2 = ctx.DBItem;
foreach (var item in q2)
{
item.DBItemList.Load(); // 觸發第二次查詢
foreach (var child in item.DBItemList) Console.WriteLine(child.NameID);
}

-- 單行註解. (如 it.ItemValue);| 類別 | 運算子 | 備註 |
|---|---|---|
| 算術 | + - * / % - (一元負號) | |
| 比較 | = > >= < <= != <> | |
IS NULL IS NOT NULL | 空值判斷 | |
| 字串比較 | LIKE NOT LIKE | % (多字元) _ (單字元) [] (字元集合) [^] (排除集合) |
| 邏輯 | AND && OR || NOT ! | |
| 區間 | BETWEEN ... AND ... NOT BETWEEN | 包含邊界 |
IN {v1,v2,...} NOT IN | 集合成員資格 | |
EXISTS (subquery) NOT EXISTS | 子查詢存在性 | |
| 分頁 (原生) | SKIP n LIMIT m | ESQL 原生分頁語法 |
| 運算 | 關鍵字 | 行為 | 範例 |
|---|---|---|---|
| 聯集 | UNION / UNION ALL | UNION 去重,UNION ALL 保留重複 | (SELECT ... WHERE id='c' OR id='a') UNION (SELECT ... WHERE id='c' OR id='b') |
| 差集 | EXCEPT | 從左集合移除右集合相同項 | (SELECT ... WHERE id='c' OR id='a') EXCEPT (SELECT ... WHERE id='c' OR id='b') |
| 交集 | INTERSECT | 取兩集合共同項 | (SELECT ... WHERE id='c' OR id='a') INTERSECT (SELECT ... WHERE id='c' OR id='b') |
| 任一元素 | ANYELEMENT(collection) | 取集合第一個元素 | ANYELEMENT(SELECT VALUE it FROM ctx.DBItemList AS it WHERE it.ItemID='a') |
| 重疊檢查 | OVERLAPS | 兩集合是否有交集 (回傳 Boolean) | (SELECT ... WHERE id='c' OR id='b') OVERLAPS (SELECT ... WHERE id='a' OR id='b') |
| 去重 | SET(collection) | 移除集合重複項 (等同 DISTINCT) | SET(SELECT VALUE it.ItemID FROM ctx.DBItemList AS it) |

Entity SQL 雖以字串為基礎,但提供了豐富的型別系統、物件導向查詢能力、繼承多型支援,並能無縫對接資料庫原生函數。配合 ObjectQuery 的 LINQ 風格建構方法與 ToTraceString() 除錯,開發者可在動態查詢、複雜報表、大量資料操作等場景中靈活運用,與 LINQ to Entities 互補共存。