Expression Trees 參數簡化查詢

ASP.NET MVC 引入了 ModelBinder 技術,讓我們可以在 Action 中以強類型參數的形式接收 Request 中的數據,極大的方便了我們的編程,提高了生產力。在查詢 Action 中,我們可以將?Expression Trees?用作參數,通過自定義的 ModelBinder 動態自動構建查詢表達式樹,進一步發揮 MVC 的威力,簡化編碼工作。

?

MVC 查詢和存在的不足

下面是一個查詢 Employee 的 Action,在 MVC 項目中經常可以見到:

public ActionResult Index(string firstName, string lastName, DateTime? birthday, bool? sex) {var employees = repository.Query();if (firstName.IsNotNullAndEmpty()) employees = employees.Where(e => e.FirstName.Contains(firstName));if (firstName.IsNotNullAndEmpty()) employees = employees.Where(e => e.LastName.Contains(lastName));if (birthday.HasValue) employees = employees.Where(e => e.Birthday.Value.Date == birthday.Value.Date);if (sex.HasValue) employees = employees.Where(e => e.Sex == sex);return View(employees);
}

得益于 MVC 的綁定技術,我們可以簡單通過 Action 的參數來獲取請求的值,很少再使用 Request["XXXX"] 的方式。

?

仔細觀察,會發現上面這個 Action 中充斥著大量 if 判斷,以致代碼行數比較多,不是特別清晰。

?

public ActionResult Index2(string firstName, string lastName, DateTime? birthday, bool? sex) {var employees = repository.Query().WhereIf(e => e.FirstName.Contains(firstName), firstName.IsNotNullAndEmpty()).WhereIf(e => e.LastName.Contains(lastName), lastName.IsNotNullAndEmpty()).WhereIf(e => e.Birthday.Value.Date == birthday.Value.Date, birthday.HasValue).WhereIf(e => e.Sex == sex, sex.HasValue);return View("Index", employees);
}

代碼相清晰了許多,我之前的幾個 MVC 項目中也是這樣處理的。

但時間一長,我逐步也發現了這種方式一些不足之處:

  1. 首先,網站中有很多類似的查詢,如Customer、Order、Product 等等。而且大致也有點規律:字符串的一般模糊查詢,時間日期類的一般按日期查詢(忽略時間),其它類型則相等查詢。不同 Model 查詢的 Action 編碼總有八、九分相似,但又不是簡單的重復,卻又難以重構
  2. 需求變動,如增加一個查詢條件,修改 View 是必須的,但也要修改 Action,增加一個參數,還要加一行 Where 或 WhereIf。簡單變動卻多處修改,煩人啊,而且這種需求變動又是比較頻繁的,尤其是在項目初期。若能只修改 View 而不修改 Action 就爽了。

思考后,我決定使用?Expression Trees?作為查詢 Action的參數來彌補這些不足。

使用 Expression<Func<T, bool>> 作為 Action 的參數

public ActionResult Index3(Expression<Func<Employee, bool>> predicate) {var employees = repository.Query().Where(predicate);return View("Index", employees);
}

將?Expression Trees?作為 Action 的唯一的參數(暫不考慮分頁、排序等),將所有的查詢條件都統一匯集至? predicate 參數。

所有的查詢(不管是 Employee 還是 Customer)都使用如上代碼。其它實體查詢只需修改參數的類型,如 Customer 查詢改為 Expression<Func<Customer, bool>> 。

如上修改代碼后,直接運行會報錯,因為 MVC 中默認的數據綁定器 DefaultModelBinder 不能正確綁定 Expression<Func<T, bool>> 類型的參數。

我們要新創一個新的 ModelBinder。

創建 QueryConditionExpressionModelBinder

?需要一個新的 ModelBinder 來為 Expression<Func<T, bool>> 類型的參數賦值,且命名為 QueryConditionExpressionModelBinder。

QueryConditionExpressionModelBinder 要根據上下文來自動生成查詢的?Expression Trees。主要關注的上下文有兩點:首先是當前 Model 的類型,即 typeof(T);其次是 Request 提供的值,可通過 ValueProvider 獲取。

下面給出一個粗略實現,僅用來說明這個思路是可行的:

public class QueryConditionExpressionModelBinder : IModelBinder {public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {var modelType = GetModelTypeFromExpressionType(bindingContext.ModelType);if (modelType == null) return null;var body = default(Expression);var parameter = Expression.Parameter(modelType, modelType.Name);foreach (var property in modelType.GetProperties()){var queryValue = GetValueAndHandleModelState(property, bindingContext.ValueProvider, controllerContext.Controller);if (queryValue == null) continue;Expression proeprtyCondition = null;if (property.PropertyType == typeof (string)){if (!string.IsNullOrEmpty(queryValue as string)){proeprtyCondition = parameter.Property(property.Name).Call("Contains", Expression.Constant(queryValue));}}else if (property.PropertyType == typeof (DateTime?)){proeprtyCondition = parameter.Property(property.Name).Property("Value").Property("Date").Equal(Expression.Constant(queryValue));}else{proeprtyCondition = parameter.Property(property.Name).Equal(Expression.Constant(queryValue));}if (proeprtyCondition != null)body = body != null ? body.AndAlso(proeprtyCondition) : proeprtyCondition;}if (body == null) body = Expression.Constant(true);return body.ToLambda(parameter);}/// <summary>/// 獲取 Expression<Func<TXXX, bool>> 中 TXXX 的類型/// </summary>private Type GetModelTypeFromExpressionType(Type lambdaExpressionType) {if (lambdaExpressionType.GetGenericTypeDefinition() != typeof (Expression<>)) return null;var funcType = lambdaExpressionType.GetGenericArguments()[0];if (funcType.GetGenericTypeDefinition() != typeof (Func<,>)) return null;var funcTypeArgs = funcType.GetGenericArguments();if (funcTypeArgs[1] != typeof (bool)) return null;return funcTypeArgs[0];}/// <summary>/// 獲取屬性的查詢值并處理 Controller.ModelState /// </summary>private object GetValueAndHandleModelState(PropertyInfo property, IValueProvider valueProvider, ControllerBase controller) {var result = valueProvider.GetValue(property.Name);if (result == null) return null;var modelState = new ModelState {Value = result};controller.ViewData.ModelState.Add(property.Name, modelState);object value = null;try{value = result.ConvertTo(property.PropertyType);}catch (Exception ex){modelState.Errors.Add(ex);}return value;}
}

如果不想在 Global.asax 文件中設置 Expression<Func<T, bool>> 的 ModelBinder, 可以借助用下面這個 Attribute 類:

public class QueryConditionBinderAttribute : CustomModelBinderAttribute {public override IModelBinder GetBinder() {return new QueryConditionExpressionModelBinder();}
}

Index3 簡單修改如下:

public ActionResult Index3([QueryConditionBinder]Expression<Func<Employee, bool>> predicate) { //... }

?

?

?

轉載于:https://www.cnblogs.com/wd0730/p/3213457.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/376510.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376510.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376510.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

為你的程序添加監聽器

平時在寫程序時經常會遇到監聽器&#xff0c;比如按鈕的click監聽器&#xff0c;按鍵監聽器等等。而android中的監聽器和java中的回調函數是同一個概念&#xff0c;都是在底層代碼中定義一個接口來調用高層的代碼。那么什么是回調函數呢&#xff1f;網上說的是“在WINDOWS中&am…

圖像處理

android圖像處理系列之四&#xff0d;&#xff0d;給圖片添加邊框&#xff08;上&#xff09; http://www.oschina.net/question/157182_40586 android圖像處理系列之六&#xff0d;&#xff0d;給圖片添加邊框&#xff08;下&#xff09;&#xff0d;圖片疊加 http://www.osc…

Git push 時每次都需要密碼的疑惑

2015.1.13更新&#xff1a; 在本地搭建Git服務器時&#xff0c;也是有每次操作需要密碼的情況。 是因為每次做推送動作時&#xff0c;Git需要認證你是好人。所以需要密碼。 可以在 /home/username/.ssh/authorized_keys 文件里添加你的 ssh 公鑰。一行一個。這樣就可以在你push…

ruby字符串處理

1. str"abc123"puts str[0].chr > a puts str[0] >a的ascii碼 2.中文字符串的正則表達式 文本編碼:utf-8 文件第一行&#xff1a;#encoding:urf-8 require "iconv" str"八萬"reg/(.)萬/datareg.match(str)result Iconv.i…

PHP+七牛云存儲上傳圖片代碼片段

2014年11月14日 16:37:51 第一段代碼是上傳單個圖片的,第二個是上傳多個圖片的 1 //上傳到七牛2 //單個文件3 //formname: 表單名字; pre: 圖片Url中顯示的圖片名字(也就是七牛中的key)4 public function upImage($formname, $pre)5 {6 if (empty($_FI…

【PS】Gold words tutorials 赤金字教程

material_01material_021. White background and black words.The font of "Laker" is Teenick, and "Huang" is 中國龍粗魏碑2.Open material_01 and select a part of it.Copy and paste the part part into our workspace.You can drag and move to pa…

iOS 鍵盤的關閉

iOS 鍵盤的關閉 //通過委托來放棄 “第一響應者” #pragma mark - UITextField Delegate Method -(BOOL)textFieldShouldReturn:(UITextField*)textField {[textField resignFirstResponder];return YES; } //通過委托來放棄 “第一響應者” #pragma mark - UITextView Delegat…

遞歸與分治

今天總算把第三章遞歸與分治看完了&#xff0c;呵呵&#xff0c;沒想到開頭就給我來了點打擊&#xff0c;看以后不認真學還真不行了&#xff01; 為了祝賀初戰告捷&#xff0c;把幾個簡單的題目貼上來吧&#xff0c;紀念一下&#xff01; 《整數因子分解》 大于1的正整數n可以分…

Android中的Handler機制

直接在UI線程中開啟子線程來更新TextView顯示的內容&#xff0c;運行程序我們會發現&#xff0c;如下錯 誤&#xff1a;android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.翻譯過來就是&…

初來乍到

從今天開始&#xff0c;我也加入博客園這個大家庭了&#xff0c;希望能和大家一起學習IT技術&#xff0c;共同進步。小弟初來乍到&#xff0c;望大家能多多關照&#xff01;轉載于:https://www.cnblogs.com/markwave/p/3227777.html

JQuery學習四(過濾選擇器)

&#xff1a;first選擇第一個元素。$&#xff08;“div:first”&#xff09;進行選擇第一個<div> :last 選擇最后一個最后一個元素 $&#xff08;"div:last"&#xff09;選取最后一個<div> [:not(選擇器&#xff09;] 選擇不滿足“選擇器”條件的元素 $…

160 - 1 Acid burn

環境&#xff1a;Windows XP sp3 先打開&#xff0c;看看長什么樣&#xff1a; OD載入&#xff0c;右鍵->查找->所有參考文本字串 找到Sorry,The serial is incorect 找到后就在反匯編窗口跟隨&#xff0c;往上翻&#xff1a; 0042F998 /. 55 push ebp 0…

跟樹有關的數據結構學習系列之概覽

1.Binary Search Tree&#xff08;BST&#xff09; 二叉搜索樹 2.B-Tree 3.BTree 4.B*Tree轉載于:https://www.cnblogs.com/devindong/p/3233041.html

在社會實踐中長本領

暑假回到家&#xff0c;家里要我在自家店里幫忙&#xff0c;做員工。因為我家跟舅舅家合資開了一家家禽凍品批發部&#xff0c;生意興旺&#xff0c;越做越大&#xff0c;忙得不可開交。在自家店里做員工&#xff0c;當然&#xff0c;家里人都很高興&#xff0c;我也樂意。在員…

Animating Layout Changes(展開收起)

原文地址&#xff1a;https://developer.android.com/training/animation/layout.html#add &#xff08;1&#xff09;設置布局文件&#xff1a; <LinearLayout android:id"id/container"android:animateLayoutChanges"true"... /> &#xff08;2&am…

160 - 2 Afkayas.1

環境&#xff1a; Windows Xp sp3 OD載入&#xff1a; 運行&#xff0c;然后輸入&#xff1a; 然后回到OD&#xff0c;按F12來暫停&#xff0c; 然后ALTF9回到程序領空&#xff0c;把彈出的那個錯誤消息框點掉&#xff0c;這時OD來到這里&#xff1a; 004025F9 . 68 E81…

POJ 2125 Destroying The Graph (二分圖最小點權覆蓋集+輸出最小割方案)

題意 有一個圖&#xff0c; 兩種操作&#xff0c;一種是刪除某點的所有出邊&#xff0c;一種是刪除某點的所有入邊&#xff0c;各個點的不同操作分別有一個花費&#xff0c;現在我們想把這個圖的邊都刪除掉&#xff0c;需要的最小花費是多少。 思路 很明顯的二分圖最小點權覆蓋…

160 - 3 Afkayas.2

環境&#xff1a; Windows xp sp3 這次的目標有兩個&#xff1a; 1.去除Nag窗口 2.找出Serial的算法 1.這次去除Nag窗口用了另外兩個程序&#xff1a; &#xff08;1&#xff09;VBLocalize v1.1.0.0 &#xff08;2&#xff09;UltraEdit &#xff08;3&#xff09;VBEx…

class threading.Thread()說明:

class threading.Thread()說明&#xff1a; class threading.Thread(groupNone, targetNone, nameNone, args(), kwargs{}) This constructor should always be called with keyword arguments. Arguments are: group should be None; reserved for future extension when a Th…

并行編程——內存模型之順序一致性

1 定義 Sequential consistency , 簡稱 SC&#xff0c;定義如下 … the result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequen…