LINQ 將查詢提升為 .NET 一等公民,支持 XML、資料庫、對象等多種數據源。對比傳統 O/R Mapping 框架,LINQ 在複雜查詢表達力與性能上均有突破。
以 Department(部門)、Employee(員工)兩表為例。
var q = from e in db.Employees
where e.EmployeeName.StartsWith("張")
select e;
// 生成 SQL: WHERE EmployeeName LIKE @p0 (+ '%')
同理支持 Contains、EndsWith,均能轉換為高效 SQL LIKE。
// 手寫嵌套
var q = from e in db.Employees
where e.DepId == (from d in db.Departments
where d.DepName == "市場部"
select d).Single().DepId
select e;
// SQL: WHERE DepId = (SELECT DepId FROM Department WHERE DepName = @p0)
// 使用導航屬性(自動轉為 LEFT JOIN)
var q = from e in db.Employees
where e.Department.DepName == "市場部"
select e;
// SQL: LEFT OUTER JOIN Department ...
嵌套查詢在特定場景下更可控;導航屬性更簡潔但生成連接,需根據執行計劃選擇。
var q = from d in db.Departments
select new {
depid = d.DepId,
depname = d.DepName,
depsalary = d.Employees.Sum(e => e.EmployeeSalary)
};
// SQL: 子查詢計算 SUM,極其優雅高效
Average()、Max() 直接下推 SQL 聚合函數,僅返回標量。Where()、OrderBy() 配合 Lambda,生成參數化 SQL。Skip(10).Take(10) 轉換為 ROW_NUMBER() OVER (...) 窗口函數,完全在資料庫端分頁,避免全表拉取。ToList()、ToArray() 觸發延遲加載,獲取具體集合。| 方式 | 耗時 |
|---|---|
| SqlDataReader (手寫 SQL) | 0.65 秒 |
| LINQ to SQL (原始) | 21.2 秒 |
| LINQ to SQL (共享連接) | ~21 秒(連接池使優化無效) |
| LINQ to SQL (CompiledQuery) | ~1 秒 |
關鍵發現:原始 LINQ 每次查詢都構建 Expression 樹與 Func 委託,開銷巨大。CompiledQuery.Compile 預編譯查詢計劃,性能逼近手寫 ADO.NET。
LINQ 更新採用樂觀並發:SubmitChanges 為 Dirty 對象生成 WHERE 條件包含所有原始列值,防止並發衝突。但導致計數器類更新(Count = Count + 1)在高並發下失敗,因 SQL 變為 SET Count = @new WHERE Count = @old,舊值已變則更新 0 行。
LINQ 禁用主鍵更新(需刪除重建),嚴謹但偶爾不便。選擇 LINQ 即選擇開發效率與類型安全,性能可通過 CompiledQuery 等手段彌補,總體可接受。