首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

C# LINQ 查詢語法完整指南:從語法基礎到投影與延遲執行

     編輯:Oracle 資料庫相關知識

什麼是查詢語法

查詢語法是 C#/VB 提供的**聲明式簡化寫法**,用於表達 LINQ 查詢。編譯器會將其轉譯為擴充方法呼叫 (Lambda 運算式),提供完整 IntelliSense 與編譯期檢查。

基本結構


// 查詢語法
var query = from p in people
            where p.LastName.StartsWith("G")
            orderby p.FirstName
            select p;

// 等價方法語法
var query = people
    .Where(p => p.LastName.StartsWith("G"))
    .OrderBy(p => p.FirstName)
    .Select(p => p);

核心子句詳解

from - 資料來源與範圍變數


from p in people  // p 為範圍變數,代表 people 中每個 Person 物件
from o in orders  // 多來源可串聯 (交叉聯結)
join d in departments on p.DeptId equals d.Id  // 內部聯結
from c in p.Children.DefaultIfEmpty()  // 左外部聯結

where - 篩選條件


where p.Age > 18 && p.IsActive
where p.Tags.Contains("vip")  // 集合包含
where new[] {1,2,3}.Contains(p.Type)  // IN 條件

orderby - 排序


orderby p.LastName, p.FirstName descending
orderby p.Salary descending, p.HireDate

select - 投影與結果構形


// 完整物件投影
select p

// 單一欄位投影 → IEnumerable
select p.FirstName

// 匿名型別投影 → IEnumerable<匿名型別>
select new { p.Id, FullName = p.FirstName + " " + p.LastName, p.Email }

// 具名型別投影 (物件初始化器)
select new PersonDto { 
    Id = p.Id, 
    FullName = p.FirstName + " " + p.LastName,
    Email = p.Email 
}

// 計算欄位投影
select new { 
    p.Id, 
    TotalRevenue = p.OrderDetails.Sum(od => od.UnitPrice * od.Quantity) 
}

group by - 分組


// 簡單分組
from p in people
group p by p.DeptId into g
select new { DeptId = g.Key, Count = g.Count(), AvgAge = g.Average(p => p.Age) }

// 多鍵分組
group p by new { p.DeptId, p.JobGrade } into g
select new { g.Key.DeptId, g.Key.JobGrade, Count = g.Count() }

let - 子查詢/暫存計算


from p in people
let fullName = p.FirstName + " " + p.LastName
let orderCount = p.Orders.Count()
where orderCount > 5
select new { p.Id, fullName, orderCount }

join - 內部聯結


from p in people
join d in departments on p.DeptId equals d.Id
select new { p.Name, DeptName = d.Name }

// 群組聯結 (左外部聯結效果)
from d in departments
join p in people on d.Id equals p.DeptId into dp
from p in dp.DefaultIfEmpty()
select new { d.Name, EmpName = p != null ? p.Name : "無員工" }

針對資料庫的查詢語法 (LINQ to SQL / Entity Framework)

IQueryable 與表達式樹

查詢語法建構 IQueryable<T>,**延遲執行**,最終轉譯為 SQL 在資料庫執行。


// 僅建構查詢,未執行
var query = from p in db.Products
            where p.UnitPrice > 99
            select p;

// 觸發執行:ToList(), ToArray(), foreach, Count(), First()
var list = query.ToList();  // 立即執行 SQL

投影優化:只選需要欄位


// 完整實體 → SELECT * (所有欄位)
var q1 = from p in db.Products where p.UnitPrice > 99 select p;

// 投影 DTO → 只 SELECT 指定欄位
var q2 = from p in db.Products 
         where p.UnitPrice > 99 
         select new ProductDto { Id = p.ProductId, Name = p.ProductName, Price = p.UnitPrice };

// 進階:計算欄位下推資料庫
var q3 = from p in db.Products
         where p.UnitPrice > 99
         select new {
             p.ProductId,
             p.ProductName,
             p.UnitPrice,
             TotalRevenue = p.OrderDetails.Sum(od => od.UnitPrice * od.Quantity)
         };

延遲執行與立即執行

類型操作行為
延遲 (Deferred)Where, Select, OrderBy, GroupBy, Join, Skip, Take建構查詢計畫,不執行
立即 (Immediate)ToList(), ToArray(), ToDictionary(), ToLookup()立即執行,回傳記憶體集合
立即First(), FirstOrDefault(), Single(), SingleOrDefault()立即執行,回傳單一元素
立即Count(), Any(), All(), Sum(), Average(), Max(), Min()立即執行,回傳純量
立即foreach 迴圈開始迭代時執行

進階技巧

查詢組合


var baseQuery = db.Products.Where(p => p.UnitPrice > 50);

// 動態加入條件
if (categoryId.HasValue)
    baseQuery = baseQuery.Where(p => p.CategoryId == categoryId);

if (inStockOnly)
    baseQuery = baseQuery.Where(p => p.UnitsInStock > 0);

var result = baseQuery.ToList();  // 單一 SQL 執行

分頁查詢


var page = query
    .OrderBy(p => p.ProductName)
    .Skip((pageIndex - 1) * pageSize)
    .Take(pageSize)
    .ToList();

非同步執行 (EF Core 6+)


var list = await query.ToListAsync();
var count = await query.CountAsync();
var first = await query.FirstOrDefaultAsync();

常見陷阱

陷阱錯誤現象解決
多次列舉foreach 兩次執行兩次 SQL先 .ToList() 緩存
N+1 查詢導航屬性觸發大量 SQL.Include() 或 投影選取
客戶端評估無法轉譯 SQL 的運算在記憶體執行改寫為可轉譯運算、或明確 AsEnumerable()
匿名型別跨方法無法作為回傳型別定義 DTO/Record 型別

C# 9+ Record 型別投影


public record ProductDto(int Id, string Name, decimal Price);

var query = from p in db.Products
            where p.UnitPrice > 99
            select new ProductDto(p.ProductId, p.ProductName, p.UnitPrice);
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved