ModelBinder
我们在利用asp.net mvc做些服务端操作时,例如增删改等,很多时候都需要和数据库打交道,要想把数据提交给数据库,第一个条件就是获取页面的表单值。在传统的asp.net中,获取值是非常容易的,因为很多都是些服务器端控件,而asp.net mvc中获取值并不是靠服务器控件的属性来获取。我们来看一下用户新增一则留言的处理方式,当然这里为了简单,只是让用户输入标题和内容,其它的略过。
第一:创建新增留言的partial view,有点类似asp.net中的用户控件。
.Models.GuestBookInfo>" %>
<script language ="javascript" type ="text/javascript" src ="http://www.cnblogs.com/Scripts/jquery-1.3.2.min.js"></script>
<script language ="javascript" type ="text/javascript" >
function check() {
var IsOK = false;
var err = "";
if (jQuery.trim($("#sTitle").val()) == "") {
err += "请输入留言标题\n";
}
if (jQuery.trim($("#sContent").val()) == "") {
err += "请输入留言内容\n";
}
if (err != "") {
alert(err);
return false;
}
return true;
}
</script>
<% using (Html.BeginForm())
{%>
<fieldset>
<p>
<label for="Title">
标题:</label>
<%= Html.TextBox("sTitle", Model.sTitle)%>
</p>
<p>
<label for="EventDate">
内容:</label>
<%=Html.TextBox("sContent", Model.sContent)%>
</p>
<p>
<input type="submit" onclick ="return check();" value="Save" />
</p>
</fieldset>
<% }
%>
第二:Controller类的修改。
说明:下面的部分代码可以会让大家迷惑,ViewData["flag"]是啥东西,其实这是我为了保存一个操作处理结果,因为提交留言后往往会转向到另外的View中,例如留言列表页,这时在用户提交操作后,应该反馈给用户执行的结果(成功还是失败),这里有两种情况:
(1):提交后View不切换,则利用ViewData["flag"]就可以完成,在页面中判断ViewData["flag"]是否存在,如果存在则根据对应的值显示不同的提示语。
(2):提交后View切换,例如转到Index.aspx中,此时ViewData就不能胜任了,我只能给GuestBookInfo的基类Message属性赋值,在index.aspx页面判断Message的值,分别做出对应处理。这时有两种方法:
1>:利用return View("Index", models);这种方法页面的地址并不会发生变化,当然页面内容会变化。
2>:return RedirectToAction("Index");这种方法链接和内容都会变化。这是比第一种方法好的地方。
问题: index中的Model由于是一个记录集,为了实现如上的提示语功能,不得不给每个记录对象的Message赋值,如果不这样做,在View中很难知道哪一个对象的Message属性是我们赋的值。不知道大家在面临这种问题时都是如何处理的,请多多指教。
public ActionResult Create(GuestBookInfo model)
{
try
{
inter.Add(model);
var models = inter.FindAllInfo();
ViewData["flag"] = 1;
//model.Message = "已经成功创建";
foreach (var i in models)
{
i.Message = "已经成功创建";
}
return View("Index", models);
//return RedirectToAction("Index");
}
catch(Exception ex)
{
ModelState.AddModelError("ex",ex);
return View(model);
}
}
留言列表页的View部分代码:(提示语部分),这里取记录集第一个对象的Message值。
在上面代码中代码中,并没有显示的给出model赋值,但实际上程序运行时,会自动把表单的相关值赋给对象。这点看起来特别神奇,MVC 为我们提供了一个自动化的操作,这一切都归功于IModelBinder 接口,系统默认会找它DefaultModelBinder来完成这一神圣的任务。DefaultModelBinder 内部通过大量的反射完成最终的赋值操作,基本上能适应开发所需。至于如何实现大家可以到网上去搜索下资料,既然有默认的,我们也可以自定义ModelBinder。
1:创建GuestBookBinder,让它继承IModelBinder,并实现其方法。这里我们可以根据实际业务需要,修改方法,这里只是一个简单实现。
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var info = bindingContext.Model ?? new GuestBookInfo();
var properties = bindingContext.ModelType.GetProperties();
foreach (var item in properties)
{
if (bindingContext.PropertyFilter(item.Name))
{
var result = bindingContext.ValueProvider[item.Name];
if (null == result)
{ break; }
var value = result.ConvertTo(item.PropertyType);
item.SetValue(info, value, null);
}
}
return info;
}
}
2:注册GuestBookBinder,自定义Binder写好后,系统并不会自动识别,需要在应用程序初始化进行注册,ControllerActionInvoker.GetParameterValue 根据 ModelBinders.Binders.GetBinder() 来找对应的 IModelBinder,如没找到则返回默认的 DefaultModelBinder。
{
ControllerBuilder.Current.DefaultNamespaces.Add("GuestBook.MVC.Controller");
ModelBinders.Binders.Add(typeof(GuestBookInfo ), new GuestBookBinder ());
RegisterRoutes(RouteTable.Routes);
}
3:除了上面的注册方法外,可以直接把 ModelBinderAttribute 用在对应的实体上 ,但不推荐这样做。
public class GuestBookInfo
4:除了由 ControllerActionInvoker.GetParameterValue() 自动完成 BindModel 操作外,还提供了两个方法:这两个方法唯一的区别在于,TryUpdateModel不会抛异常,前者会。
1>:Controller.UpdateModel()
2>:Controller.TryUpdateModel()
示例:例如更新一则留言时,我们可以这样写:
public ActionResult Edit(int id, FormCollection formValues)
{
GuestBookInfo model = new GuestBookInfo();
model.ID = id;
model = inter.GetInfo(model);
UpdateModel(model );
inter.Edit(model);
return RedirectToAction("Index");
}
总结:这篇文章主要总结了些IModelBinder 接口的作用,以及如何自定义Binder类。
注:本文参考:http://www.rainsts.net/article.asp?id=779