所謂動態查詢,是指查詢條件或查詢值都是在運行時才能確定的查詢。這就意味著我們不能hard-code定義查詢變量(query variable),只有根據查詢時傳遞的條件來拼湊。下面我們看看幾組不同條件組合的查詢。
1) 用戶輸入查詢條件:City為”London”且ContactName包含”Thomas”
01 public IQueryable<Customer> GetCustomers(string city, string contactName)
02 {
03 var context = GenerateContext();
04 IQueryable<Customer> result = context.Customers;
05
06 if (!string.IsNullOrEmpty(city))
07 {
08 result = result.Where(c => c.City == city);
09 }
10
11 if (!string.IsNullOrEmpty(contactName))
12 {
13 result = result.Where(c => c.ContactName.Contains(contactName));
14 }
15
16 return result;
17 }
2) 用戶輸入查詢條件:City為”London”或”Paris”
由於Where和Where的連接是表示AND的關系,所以我們無法用1)的方法來表達這個查詢。有一個可以利用的query operator是Union:
01 var context = GenerateContext();
02 IQueryable<Customer> result = null;
03 string[] cities = { "London", "Paris" };
04
05 foreach (string item in cities)
06 {
07 string tmp = item;
08 result = result == null ?
09 context.Customers.Where(c => c.City == tmp) :
10 result.Union(context.Customers.Where(c => c.City == tmp));
11 }
12
13 context.Log = Console.Out;
14 Console.WriteLine(result.Count());
雖然結果符合我們的要求,但是通過輸出的SQL語句,你可以看到對於這種方式構造的Expression Tree,SQL並沒有我們想要的精簡(我們期望的是t0.City=’London’ OR t0.City=’Paris’)。輸出SQL如下:
01 SELECT COUNT(*) AS [value]
02 FROM (
03 SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[Cont
04 actTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[
05 Country], [t0].[Phone], [t0].[Fax]
06 FROM [dbo].[Customers] AS [t0]
07 WHERE [t0].[City] = @p0
08 UNION
09 SELECT [t1].[CustomerID], [t1].[CompanyName], [t1].[ContactName], [t1].[Cont
10 actTitle], [t1].[Address], [t1].[City], [t1].[Region], [t1].[PostalCode], [t1].[
11 Country], [t1].[Phone], [t1].[Fax]
12 FROM [dbo].[Customers] AS [t1]
13 WHERE [t1].[City] = @p1
14 ) AS [t2]
另外的方法就是是根據所有查詢條件和查詢值,給Where構造一個Expession。首先來看看Where的簽名:
1 IQueryable<T> Where<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate)
對於這樣一個Expression,如果你熟悉Expression Tree的構造方法,完全可以自己寫。《C# 3.0 In Nutshell》的作者寫了一個小巧的PrediateBuilder,可以幫助我們用lambda expression快速構造這樣的expression:
01 var context = GenerateContext();
02 Expression<Func<Customer, bool>> filter = PredicateBuilder.False<Customer>();
03 string[] cities = { "London", "Paris" };
04
05 foreach (string item in cities)
06 {
07 string tmp = item;
08 filter = filter.Or(c => c.City == tmp);
09 }
10
11 context.Log = Console.Out;
12 context.Customers.Where(filter).Count().Dump();
輸出的SQL也是我們期待的結果
1 SELECT COUNT(*) AS [value]
2 FROM [dbo].[Customers] AS [t0]
3 WHERE ([t0].[City] = @p0) OR ([t0].[City] = @p1)
最後,如果你對以前拼接SQL語句的方式還是念念不忘的話,可以去用Dynamic LINQ,參考SocttGu’s Blog。