2014年10月14日 星期二

[iThome 第七屆鐵人賽 22] 搜索頁面 - Service層的工作 -動態產生Linq條件

在上一篇介紹完了會使用到的ViewModel之後,接下來就是實際的商業邏輯,也就是實際做搜索和產生資料的部份。

在這一篇,將會介紹如何透過Service層和ViewModel的搭配,讓使用起來變的更加方便。

功能描述

Service的流程大概如下:

  1. 依照SearchViewModel裡面的欄位去做DB搜索
  2. 得出的結果將會用Automapper轉成要的SearchResultViewModel,並且透過PagedList.Mvc的方式把資料包住
  3. View方面的呈現 - 搜索表單可以做成通用的Partial

由於Service要做的事情也滿多的,因此整個Service層的實作會分幾篇來介紹。

Service依照SearchViewModel裡面的欄位去做搜索

這個部份其實要拆成兩塊:

  1. 動態組裝Linq條件 - Linq搜索的好處是強型別的條件,但是當我們希望Service自動依照欄位去做搜索的時候,Linq就不方便使用了。因此,我們需要先瞭解如何動態組裝Linq條件
  2. 透過Reflection取得搜索欄位和條件 - SearchViewModelBase裡面有必要有的欄位(例如目前第幾頁,用什麽欄位做排序,詳細請看上一篇),但是這些欄位和實際搜索的DB沒有關係,SearchViewModelBase 裡面是方便做分頁用的,而實際的搜索條件是每一個繼承下來需要的,因此透過Reflection可以做到。

動態組裝的Linq

基本上,動態組裝Linq條件有3種方法:

  1. 使用PredicateBuilder - 強型別的方式組裝linq條件
  2. 使用Dynamic Linq Library - 用string的方式組裝Linq
  3. 自己組裝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的方法。

  1. Nuget安裝指令:Install-Package LinqKit
  2. Nuget頁面:LinqKit
  3. 官方介紹Predicate Builder:Dynamically Composing Expression Predicates
  4. 官方介紹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,方便使用

Dynamic Linq Query非常適合做那種通用型的處理,例如不知道欄位有什麽的情況下。但是壞處是,喪失了強型別的好處。不過這個非常適合框架處理搜索條件。

結語


本來這一篇還要介紹框架Service層如何搭配Dynamic Linq Query來使用,不過在介紹動態Linq條件的時候,篇幅有點長,因此就把Service層的實作留在下一篇。


沒有留言 :

張貼留言