ASP.NET MVC & EF 構建智能查詢 二、模型的設計與ModelBinder
在第一篇中,我講解了我們要做智能查詢的原因,以及基本的解決方案設計。從這篇開始我們開始講解它的實現過程。
其實在寫這一系列文章之初,我其實是想由底至上去講解,但是我又整理了一遍代碼才發現,其實如果不了解最表面的東西,也是不太好深入的。
所以我們的第二篇文章就來講一下我們這個智能查詢框架中最淺,但也是使用最頻繁的部分,也就是Model。
首先我們的Entity 或者說數據庫的結構如下:
另外如下面代碼,我們有一個用于傳遞name=value對,及查詢謂詞的model:
{
using(var db=new DbEntities())
{
var list = db.Users.Where(model).ToList();
return View(list);
}
}
我命名之為QueryModel。它由Action的參數傳入,再傳入EF的Where擴展方法,它是構建Lambda表達式的原型,上面是我們的一個通用的查詢Action的代碼。
而QueryModel的代碼為:
QueryModel的唯一一個屬性Items,是一個ConditionItem的集合,它里面存著所有的查詢條件。而ConditionItem里面的屬性:
- Field表示要查詢的目標屬性的名稱,我們的設定它是支持子屬性查詢的,例如 “Profile.MyUser.Id”。
- Method則是當前使用的謂詞,它是QueryMethod的一個枚舉值。
- OrGroup是一個字符串,是一個OrGroup的多個表達式將會以Or操作符進行關聯,然后再And。
- Prefix是一個分類的前綴,我們假定一個Action可能處理多個查詢條件組的時候為了分開這些查詢條件而加的屬性。
- Value則是這個表達試的值。
而我們在頁面上類似:
姓名:<input id="Name" name="[Like]Name" type="text" value="" />
Email:<input id="Email" name="[Equal]Email" type="text" value="" /><br />
Id: <input id="Id" name="[Equal]Id" type="text" value="" />
生日: <input id="Birthday" name="[Equal]Birthday" type="text" value="" /><br />
<input type="submit" value="查詢" />
</form>
這樣的表單,我們提交到服務器端,我們要進行ASP.NET MVC 中IModelBinder的轉換,要將querystring 或 postdata中的KeyValuePair轉換為我們的ConditionItem。
當然,我們除了謂詞Method之外,還有Prefix以及OrGroup要從客戶端提交過來所以我們就要制定一個規則,我們約定:
- 中括號[]中的為查詢謂詞
- 小括號()中的為前綴Prefix
- 大括號{}中的為OrGroup
我們可以通過以下IModelBinder的實現將Request.QueryString或Request.Form中的值轉換到QueryModel中:
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = (QueryModel)(bindingContext.Model ?? new QueryModel());
var dict = controllerContext.HttpContext.Request.Params;
var keys = dict.AllKeys.Where(c => c.StartsWith("["));//我們認為只有[開頭的為需要處理的
if (keys.Count() != 0)
{
foreach (var key in keys)
{
if (!key.StartsWith("[")) continue;
var val = dict[key];
//處理無值的情況
if (string.IsNullOrEmpty(val)) continue;
AddSearchItem(model, key, val);
}
}
return model;
}
/// <summary>
/// 將一組key=value添加入QueryModel.Items
/// </summary>
/// <param name="model">QueryModel</param>
/// <param name="key">當前項的HtmlName</param>
/// <param name="val">當前項的值</param>
public static void AddSearchItem(QueryModel model, string key, string val)
{
string field = "", prefix = "", orGroup = "", method = "";
var keywords = key.Split(']', ')', '}');
//將Html中的name分割為我們想要的幾個部分
foreach (var keyword in keywords)
{
if (Char.IsLetterOrDigit(keyword[0])) field = keyword;
var last = keyword.Substring(1);
if (keyword[0] == '(') prefix = last;
if (keyword[0] == '[') method = last;
if (keyword[0] == '{') orGroup = last;
}
if (string.IsNullOrEmpty(method)) return;
if (!string.IsNullOrEmpty(field))
{
var item = new ConditionItem
{
Field = field,
Value = val.Trim(),
Prefix = prefix,
OrGroup = orGroup,
Method = (QueryMethod) Enum.Parse(typeof (QueryMethod), method)
};
model.Items.Add(item);
}
}
}
當然我們還要在Global.asax中添加:
這樣我們就可以在Action中獲取到相應的查詢條件了: