☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
concept-backend 中級

LINQ 延遲執行與效能陷阱

什麼是延遲執行?

var query = db.Users.Where(u => u.Age > 18); // ← 這行不會查資料庫!
// query 只是一個「查詢計畫」,還沒執行

var list = query.ToList(); // ← 這行才真的送 SQL 到資料庫

延遲執行(Deferred Execution):LINQ 查詢只在你「列舉」時才執行。


IEnumerable vs IQueryable

IEnumerable IQueryable
執行位置 記憶體(C# 端) 資料庫(SQL 端)
篩選時機 先全部載入,再篩選 篩選條件轉成 SQL
適合 記憶體中的集合 EF Core 資料庫查詢
// ❌ 效能災難:載入全部使用者到記憶體再篩選
IEnumerable<User> users = db.Users; // 載入 100 萬筆
var adults = users.Where(u => u.Age > 18); // 在 C# 記憶體中篩選

// ✅ 正確:篩選條件在資料庫執行
IQueryable<User> users = db.Users; // 還沒查
var adults = users.Where(u => u.Age > 18).ToList(); // WHERE age > 18(SQL 端篩選)

常見陷阱

1. 多次列舉

var query = db.Users.Where(u => u.IsActive);

var count = query.Count();     // ← 查一次 DB
var list = query.ToList();     // ← 又查一次 DB!
var first = query.First();     // ← 又查一次!

// ✅ 先 ToList(),再對記憶體操作
var list = query.ToList();     // 查一次
var count = list.Count;        // 記憶體操作
var first = list.First();      // 記憶體操作

2. Select N+1

// ❌ 每個 Order 都會查一次 Customer(N+1 問題)
var orders = db.Orders.ToList();
foreach (var o in orders) {
    Console.WriteLine(o.Customer.Name); // 每次都查 DB!
}

// ✅ Include 一次載入
var orders = db.Orders.Include(o => o.Customer).ToList();

3. 在迴圈裡用 LINQ 查詢

// ❌ 每次迴圈都送一次 SQL
foreach (var id in userIds) {
    var user = db.Users.FirstOrDefault(u => u.Id == id); // N 次查詢
}

// ✅ 一次撈完
var users = db.Users.Where(u => userIds.Contains(u.Id)).ToList(); // 1 次查詢

ToList() vs AsEnumerable() vs AsNoTracking()

.ToList()         // 立即執行,載入到記憶體(List<T>)
.AsEnumerable()   // 切換到 LINQ to Objects(之後的操作在記憶體)
.AsNoTracking()   // 不追蹤實體變化(唯讀查詢效能提升 30-50%)

原則:只讀不改的查詢,一律加 .AsNoTracking()

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊