asp.net mvc (三)

ModelBinder

     我们在利用asp.net mvc做些服务端操作时,例如增删改等,很多时候都需要和数据库打交道,要想把数据提交给数据库,第一个条件就是获取页面的表单值。在传统的asp.net中,获取值是非常容易的,因为很多都是些服务器端控件,而asp.net mvc中获取值并不是靠服务器控件的属性来获取。我们来看一下用户新增一则留言的处理方式,当然这里为了简单,只是让用户输入标题和内容,其它的略过。

   第一:创建新增留言的partial view,有点类似asp.net中的用户控件。   

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<GuestBook.Common 

.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属性是我们赋的值。不知道大家在面临这种问题时都是如何处理的,请多多指教。
    

 [AcceptVerbs(HttpVerbs.Post)]
       
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值。

 <% =Html.MessageBox (Model.First ())%>


       在上面代码中代码中,并没有显示的给出model赋值,但实际上程序运行时,会自动把表单的相关值赋给对象。这点看起来特别神奇,MVC 为我们提供了一个自动化的操作,这一切都归功于IModelBinder 接口,系统默认会找它DefaultModelBinder来完成这一神圣的任务。DefaultModelBinder 内部通过大量的反射完成最终的赋值操作,基本上能适应开发所需。至于如何实现大家可以到网上去搜索下资料,既然有默认的,我们也可以自定义ModelBinder。

        1:创建GuestBookBinder,让它继承IModelBinder,并实现其方法。这里我们可以根据实际业务需要,修改方法,这里只是一个简单实现。

public class 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。    

protected void Application_Start()
        {
            ControllerBuilder.Current.DefaultNamespaces.Add(
"GuestBook.MVC.Controller");
            ModelBinders.Binders.Add(
typeof(GuestBookInfo ), new GuestBookBinder ());
            RegisterRoutes(RouteTable.Routes);
        }

    
      3:除了上面的注册方法外,可以直接把 ModelBinderAttribute 用在对应的实体上 ,但不推荐这样做。

 

[ModelBinder(typeof(GuestBookBinder))]
public class GuestBookInfo

    
      4:除了由 ControllerActionInvoker.GetParameterValue() 自动完成 BindModel 操作外,还提供了两个方法:这两个方法唯一的区别在于,TryUpdateModel不会抛异常,前者会。 
        1>:Controller.UpdateModel()
        2>:Controller.TryUpdateModel()

      示例:例如更新一则留言时,我们可以这样写:
     

[AcceptVerbs(HttpVerbs.Post)]
       
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

posted on 2009-08-09 22:07  min.jiang  阅读(3908)  评论(1编辑  收藏  举报