Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 為現任微軟最有價值專家 (MVP)、微軟認證講師 (MCT) 、Blogger、Youtuber:記錄軟體開發的點點滴滴 著重於微軟技術、C#、ASP .NET、Azure、DevOps、Docker、AI、Chatbot、Data Science

[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層的實作留在下一篇。


如果文章對您有幫助,就請我喝杯飲料吧
街口支付QR Code
街口支付QR Code
台灣 Pay QR Code
台灣 Pay QR Code
Line Pay 一卡通 QR Code
Line Pay 一卡通 QR Code
街口支付QR Code
支付寶QR Code
街口支付QR Code
微信支付QR Code
comments powered by Disqus