2010年7月13日 星期二

ASP.NET MVC 之 Custom ModelBinder

在 ASP.NET MVC 要取得 Form 傳遞回來的資料有幾種方式,

一、直接取用 FormCollection ,例:

[HttpPost]
public ActionResult Test(FormCollection testFormCollection)
{
    string Name = testFormCollection["Name"];
    string Title = testFormCollection["Title"];
    return View();
}
輸入畫面:

結果:


二、直接在 Function 上設定接收參數,例:

[HttpPost]
public ActionResult Test(string Name, string Title)
{
    return View();
}
輸入畫面:

結果:


三、使用 ModelBinder ,例:

[HttpPost]
public ActionResult Test(User testModelBinder)
{
    return View();
}
ViewModel: User
namespace Captcha.Models
{
    public class User
    {
        public string Name { get; set; }
        public string Title { get; set; }
    }
}
輸入畫面:

結果:


個人偏愛第三種,不然也不會有今天這篇!
使用 ViewModel 的好處是強型別可以在編譯的時候發現錯誤,
直接用 Default ModelBinder 也是蠻 OK 的,只要型別正確,
name 屬性值有對應的欄位,八九不離十都可以正確的 bind,
但是有時候為了特殊需求:

比方資料庫某一欄位紀錄值是用逗號分隔紀錄另外一個 table 的 id,
頁面我們用 checkbox 來選取,這樣在 Default ModelBinder
會將同樣 name 的 checkbox bind 成一個陣列,
如此我們便要將陣列再兜成用逗號分隔的字串,才能放進資料庫。

因此有了客製化 ModelBinder 的需求,
直覺的方式就是將 FormCollection 的內容,
依照 Model 各屬性的型別,嘗試轉型,並且將值寫入,
FormCollection 中 checkbox 就是被存成已逗號分隔的字串,
所以就直接存入。
大致上的概念就是這樣。

那首先 Custom ModelBinder 必須實做 IModelBinder
範例如下:
public class UserModelBinder : IModelBinder
{
    public object BindModel(
        ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
    {
        User result = new User();
        var tempForm = controllerContext.HttpContext.Request.Form;
        result.Name = tempForm["Name"];
        result.Title = tempForm["Title"];
        result.Checkbox = tempForm["testCheckbox"];

        return result;
    }
}
修改一下剛剛的 ViewModel: User
namespace Captcha.Models
{
    public class User
    {
        public string Name { get; set; }
        public string Title { get; set; }
        public string Checkbox { get; set; }
    }
}
輸入畫面:

結果:


結果是不是符合我們的期待呢!

P.S: 強調一下,這範例僅僅是範例,完全沒有做例外處理,請多加注意。



2 則留言:

jgedean 提到...

請問一下,假如我要把傳到資料庫的checkbox的值,把它們呈現在畫面上呢,如同最後面的圖片。
資料庫裡的checkbox欄位上有的值,在畫面呈現打勾的狀態。

JHShen 提到...

Controller

public ActionResult Index()
{
ViewBag.Test1 = "001,002,003,004,005";
ViewBag.Test2 = new string[] { "001", "002", "003", "004", "005" };
return View();
}

View

@Html.CheckBox("001", ((string)ViewBag.Test1).Contains("001"))

@Html.CheckBox("002", ((string)ViewBag.Test1).Contains("002"))

@Html.CheckBox("003", ((string)ViewBag.Test1).Contains("003"))

@Html.CheckBox("004", ((string)ViewBag.Test1).Contains("004"))

@Html.CheckBox("006", ((string)ViewBag.Test1).Contains("006"))


@Html.CheckBox("001", ((string[])ViewBag.Test2).Contains("001"))

@Html.CheckBox("002", ((string[])ViewBag.Test2).Contains("002"))

@Html.CheckBox("003", ((string[])ViewBag.Test2).Contains("003"))

@Html.CheckBox("004", ((string[])ViewBag.Test2).Contains("004"))

@Html.CheckBox("006", ((string[])ViewBag.Test2).Contains("006"))