在上一篇介紹完了會使用到的ViewModel之後,接下來就是實際的商業邏輯,也就是實際做搜索和產生資料的部份。
在這一篇,將會介紹如何透過Service層和ViewModel的搭配,讓使用起來變的更加方便。
功能描述
Service的流程大概如下:
- 依照SearchViewModel裡面的欄位去做DB搜索
- 得出的結果將會用Automapper轉成要的SearchResultViewModel,並且透過PagedList.Mvc的方式把資料包住
- View方面的呈現 – 搜索表單可以做成通用的Partial
由於Service要做的事情也滿多的,因此整個Service層的實作會分幾篇來介紹。
Service依照SearchViewModel裡面的欄位去做搜索
這個部份其實要拆成兩塊:
- 動態組裝Linq條件 – Linq搜索的好處是強型別的條件,但是當我們希望Service自動依照欄位去做搜索的時候,Linq就不方便使用了。因此,我們需要先瞭解如何動態組裝Linq條件
- 透 過Reflection取得搜索欄位和條件 – SearchViewModelBase裡面有必要有的欄位(例如目前第幾頁,用什麼欄位做排序,詳細請看上一篇),但是這些欄位和實際搜索的DB沒有關 係,SearchViewModelBase 裡面是方便做分頁用的,而實際的搜索條件是每一個繼承下來需要的,因此透過Reflection可以做到。
動態組裝的Linq
基本上,動態組裝Linq條件有3種方法:
- 使用PredicateBuilder – 強型別的方式組裝linq條件
- 使用Dynamic Linq Library – 用string的方式組裝Linq
- 自己組裝Expression Tree
自己寫Expression Tree
基本上Linq的Where條件最終組出來就是一個Expression Tree。因此如果對於Expression Tree有所瞭解可以自己動手寫。
但是基本上要會寫這個要對於整個比較瞭解,因此基本不用考慮。
使用PredicateBuilder
PredicateBuilder基本上就是可以用強型別的方式組裝Linq條件。舉例來說,我們使用Linq來寫Where條件,當Where條件寫好只好,是沒有辦法在對那個Where條件在做調整。
因此,假設我們的搜索表單有4個條件,有輸入才做搜索條件,如果條件是兩個以上,還要做and的邏輯。要用原生的linq做到這個其實是很困難的,因為Linq條件是不能在改,因此等於每一種情況都要寫一個,這個很難維護也很麻煩。
因此,PredicateBuilder就很方便,它能夠讓我們動態修改Linq條件,因此舉例來說(官方的例子):
IQueryable<Product> SearchProducts (params string[] keywords)
{
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where (predicate);
}
有一堆搜索的Keyword,每一個要以Or的方式做搜索,不是用PredicateBuilder,原生的Linq是辦不到的(如果全部要做and是做的到,但是or就沒有辦法)。
LinqKit 套件
PredicateBuilder屬於LinqKit套件的一部份,這個套件提供了一些方便處理Linq的方法。
- Nuget安裝指令:Install-Package LinqKit
- Nuget頁面:LinqKit
- 官方介紹Predicate Builder:Dynamically Composing Expression Predicates
- 官方介紹LinqKit:What is LINQKit?
PredicateBuilder非常適合在確定有那些欄位的情況下使用。例如確定有4個搜索欄位。但是,這個不適合我們使用。因為如果要寫共通處理邏輯,更本就不知道有那些搜索欄位。
Dynamic Linq Query
早期在寫Sql的時候,有時候Sql的語法使用string組裝在一起(當然要用NameParameter避免Sql injection)。這一種組裝的好處是完全可以寫好一個共用邏輯,只要符合條件的就用string concat的方式組裝條件,非常的方便。
Linq如果可以做到就好了,因此由Scot Gu介紹了一個所謂的Dynamic Linq Query – Dynamic LINQ
Dynamic Linq Query基本上就是可以讓我們用string方式組裝linq,因此(下面範例是由保哥一篇介紹的文章裡面截取):
Northwind db = new Northwind(connString);
db.Log = Console.Out;
var query =
db.Customers.Where("City == @0 and Orders.Count >= @1", "London", 10).
OrderBy("CompanyName").
Select("New(CompanyName as Name, Phone)");
Console.WriteLine(query);
Console.ReadLine();
Dynamic Linq Query
基本上這個功能並沒有包含在.Net裡面,如果要使用之前是需要到Scott Gu的那篇文章下載dll。不過有人把它包到了Nuget,方便使用
- Nuget安裝指令:Install-Package System.Linq.Dynamic
- Nuget頁面:System.Linq.Dynamic
- 官網介紹:Dynamic LINQ
- 保哥介紹:Dynamic LINQ 讓 LINQ 的世界變的更美好
Dynamic Linq Query非常適合做那種通用型的處理,例如不知道欄位有什麼的情況下。但是壞處是,喪失了強型別的好處。不過這個非常適合框架處理搜索條件。
結語
本來這一篇還要介紹框架Service層如何搭配Dynamic Linq Query來使用,不過在介紹動態Linq條件的時候,篇幅有點長,因此就把Service層的實作留在下一篇。