在 LINQ 查詢中,Expression<Func<T, bool>> (表達式樹) 與 Func<T, bool> (委派) 的選擇,直接決定查詢是在 **資料庫端執行** 還是 **記憶體中執行**。
| 特性 | Expression<Func<T,bool>> | Func<T,bool> |
|---|---|---|
| 本質 | 表達式樹 (資料結構,可序列化、可分析) | 編譯後的 IL 程式碼 (黑盒委派) |
| 適用介面 | IQueryable<T> (Entity Framework, LINQ to SQL) | IEnumerable<T> (LINQ to Objects) |
| 執行位置 | 翻譯為 SQL 在資料庫引擎執行 | 在應用程式記憶體中逐列執行 |
| 效能 | 可利用索引、只傳回所需列、網路傳輸少 | 需先把全表拉到記憶體再篩選,極慢 |
| 支援運算 | 受限於 SQL 轉譯能力 (如不支援某些 C# 方法) | 支援任意 C# 程式碼 |
// ✅ 正確:Expression,EF Core 翻譯成 WHERE name LIKE '%abc%'
var users = dbContext.Users.Where(u => u.Name.Contains("abc")).ToList();
// ❌ 錯誤:Func,先載入全表 Users 再在記憶體篩選
Func<User, bool> predicate = u => u.Name.Contains("abc");
var users = dbContext.Users.Where(predicate).ToList(); // 全表掃描!
// ✅ 正確:動態建構 Expression
var param = Expression.Parameter(typeof(User), "u");
var prop = Expression.Property(param, "Name");
var constVal = Expression.Constant("abc");
var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var call = Expression.Call(prop, method, constVal);
var lambda = Expression.Lambda<Func<User, bool>>(call, param);
var users = dbContext.Users.Where(lambda).ToList();
List<T>, Array, 已 .ToList() 的結果)。Enumerable.Where)。IQueryable 使用 lambda 直接傳遞,不要先存入 Func 變數。PredicateBuilder 或手工組裝 Expression,保持可翻譯性。LogTo),觀察生成 SQL 確認是否下推。記住:**Expression = 可翻譯 = 下推資料庫 = 高效**;**Func = 黑盒 = 記憶體篩選 = 低效**。