動態查詢指查詢條件與值在執行期才能決定,無法硬編碼 Lambda 運算式。本文展示三種常見動態查詢場景與解決方案。
public IQueryable<Customer> GetCustomers(string city, string contactName) {
var context = GenerateContext();
IQueryable<Customer> result = context.Customers;
if (!string.IsNullOrEmpty(city))
result = result.Where(c => c.City == city);
if (!string.IsNullOrEmpty(contactName))
result = result.Where(c => c.ContactName.Contains(contactName));
return result; // 鏈式 Where 即為 AND 關係
}
查詢 City 為 London 或 Paris:
string[] cities = { "London", "Paris" };
IQueryable<Customer> result = null;
foreach (string item in cities) {
string tmp = item; // 閉包陷阱避免
result = result == null
? context.Customers.Where(c => c.City == tmp)
: result.Union(context.Customers.Where(c => c.City == tmp));
}
缺點:產生 UNION SQL,非理想的 OR 條件,效能較差。
使用 PredicateBuilder(出自《C# 3.0 In Nutshell》)以 Lambda 語法構建運算式樹:
var context = GenerateContext();
Expression<Func<Customer, bool>> filter = PredicateBuilder.False<Customer>();
string[] cities = { "London", "Paris" };
foreach (string item in cities) {
string tmp = item;
filter = filter.Or(c => c.City == tmp);
}
context.Customers.Where(filter).Count().Dump();
產生理想 SQL:
SELECT COUNT(*) FROM [Customers] WHERE ([City] = @p0) OR ([City] = @p1)
| 術語 | 說明 |
|---|---|
Expression<TDelegate> | 運算式樹節點,可被 LINQ to SQL 解析為 SQL |
Func<T, bool> | 委托,僅能在記憶體中執行(LINQ to Objects) |
PredicateBuilder | 輔助類別,提供 And/Or 擴充方法組合運算式 |
關鍵差異:
context.Customers.Where(condition); // condition: Expression<Func<Customer,bool>> → 翻譯為 SQL
context.Customers.Where(condition.Compile()); // 編譯為 Func → 在記憶體篩選(效能差)
若習慣字串拼接 SQL,可使用 ScottGu 推薦的 Dynamic LINQ Library,支援字串形式查詢條件。