LINQ (Language Integrated Query) 為 C# 提供統一的資料查詢語法,支援記憶體集合、資料庫、XML 等多種資料源。本文從查詢表達式語法切入,詳細解析 from/where/select/orderby/join/group/let/into 等子句的編譯轉換、延遲執行機制與進階用法。
var articles = from article in db.Articles
where article.Name == "JIM"
select article;
from article in db.Articles 宣告範圍變數 article 遍歷 db.Articleswhere 子句對應 Where(article => article.Name == "JIM")select article 對應 Select(article => article) (退化查詢)foreach/ToList()) 才開始逐筆處理from a in db.Articles where a.Name=="JIM" where a.Age>20 select a; // 編譯為 db.Articles.Where(a=>a.Name=="JIM").Where(a=>a.Age>20)
select new { Name = article.Name, Upper = article.Name.ToUpper() };
// 建立新物件,隔離來源資料,避免副作用
from article in db.Articles select article; // 隱式,編譯器推斷 from Article article in db.Articles select article; // 顯式,需 Cast<Article>()
顯式型別用於非泛型集合 (如 ArrayList) 或需強制轉型時。
from a in db.Articles orderby a.Age descending, a.Height select a; // 編譯為 OrderByDescending(a=>a.Age).ThenBy(a=>a.Height)
OrderBy 回傳 IOrderedEnumerable<T>,支援 ThenBy/ThenByDescending 鏈式orderby 子句包含所有排序鍵from a in db.Articles
let len = a.Name.Length
orderby len
select new { Name = a.Name, Length = len };
編譯器透過匿名型別包裝原範圍變數與新變數 (透明識別碼) 實現後續子句存取 len。
join c in db.Comments on a.Name equals c.ArticleName select new { a.Name, c.Content } 右側序列緩衝,左側流式處理,小表放右側。join c in db.Comments on a.Name equals c.ArticleName into comments select new { Article=a, Comments=comments } 保留左側所有項目,無對應則為空序列。from a in db.Articles from c in db.Comments select new { a, c } 笛卡兒積,無 on 條件。from a in db.Articles where a.Name=="JIM" group a by a.Age; // 編譯為 Where(...).GroupBy(a=>a.Age, a=>a.Author) 可指定結果選擇器
將前一階段查詢結果作為新範圍變數繼續查詢:
from a in db.Articles where a.Name=="JIM"
group a by a.Age into g
select new { Age = g.Key, Count = g.Count() }
into result
orderby result.Count
select result;
可多層延續,但過度使用降低可讀性,建議拆分為多個查詢變數。
| 方法 | 行為 | 異常處理 |
|---|---|---|
First() | 第一個元素 | 空序列拋出 |
FirstOrDefault() | 第一個或 default | 不拋出 |
Single() | 唯一元素 | 零或多個拋出 |
SingleOrDefault() | 唯一或 default | 不拋出 |
ToList() | 物質化為 List | - |
Include("Navigation") | EF 急載入導航屬性 | - |
IQueryable<T> (如 EF) 生成 Expression<Func> 表達式樹,由 Provider 轉譯為 SQLIEnumerable<T> 生成 Func<T, bool> 委托,在記憶體中執行查詢表達式提供聲明式、類 SQL 的查詢體驗,編譯器將其轉換為標準查詢運算子鏈。理解 延遲執行、透明識別碼 (let/join/group into)、聯接類型 與 查詢延續 是駕馭 LINQ 的關鍵。複雜查詢建議拆分為多個變數或改用方法語法 (Where/Select...) 以提升可讀性與除錯便利性。