您的位置:首页 > 其它

MVC中的扩展点(八)模型绑定

2011-01-16 15:21 232 查看
MVC可以将用户提交的数据绑定到Action参数,我们将这个过程称之为模型绑定,在模型绑定中有两个关键:一个是值提供器,用于确定数据来源,另一个称为模型绑定器,用于确定如何将值绑定到特性的数据模型。MVC中默认的值提供器值提供器是一组实现了IValueProvider接口的类,MVC中的值提供其使用了标准的抽象工厂设计模式,其类图如下:MVC提供了四种默认的值提供器:
FormValueProvider:表单数据,对应于ASP.NET的Request.Form集合QueryStringValueProvider:查询字符串,对应于ASP.NET的Request.QueryString集合HttpFileCollectionValueProvider:文件集合,数据来源于Request.Files集合RouteDataValueProvider:路由信息,对应于RouteData.Values集合
MVC为每一个值提供器提供了一个工厂:ValueProviderFactory,ValueProviderFactoryCollection是一个值提供器工厂集合,其中的GetValueProvider方法返回与当前控制器上下文匹配的值提供器。ValueProviderFactories是一个静态类,内部封装一个ValueProviderFactoryCollection集合(Factories属性),此集合默认包含MVC中4个默认的值提供器,所以,如果我们要使用自己的值绑定器,可以通过此类的Factories属性,使用InsertItem增加新的值提供器,使用SetItem方法将某个默认值提供器替换为我们的自定义值提供器。 需要注意的是,ValueProviderFactoryCollection的GetValueProvider方法实际返回的是一个ValueProviderCollection集合,但由于此集合类同样实现了IValueProvider接口(其GetValue方法会从集合中所有的值提供器中查找符合条件的项),所以GetValueProvider的返回类型仍然为IValueProvider。MVC中默认的模型绑定器模型绑定器用于将值提供器提供的数据映射到特定的模型类型,正因为有模型绑定器的存在,才使我们可以直接使用带参数的控制器方法:绑定器可以从值提供器中获取数据,并根据控制器方法参数类型,自动实例化参数并填充数据。MVC中默认的模型绑定器类结构如下:MVC中实现四种模型绑定器:
HttpPostedFileBaseModelBinder:用于处理HttpPostedFileBase类型ByteArrayModelBinder:用于处理Byte[]类型LinqBinaryModelBinder:用于处理Linq中的Binary类型DefaultModelBinder:默认绑定器,如果某个类型没有特定的绑定器,则使用此绑定器。
ModelBinderDictionary是一个IModelBinder字典,键为类型,值为类型所对应的绑定器。DefaultBinder属性指定如果集合中不存在指定类型的绑定器时,应返回的默认绑定器,默认为DefaultModelBinder。我们可以通过此属性来指定我们自己的默认绑定器。ModelBinders是一个静态类,内部封装了ModelBinderDictionary(Binders属性),包含默认的绑定器列表。CustomModelBinderAttribute是一个用于指定自定义绑定器的特性的抽象基类,方法GetBinder用于返回一个绑定器实例。ModelBinderAttribute是CustomModelBinderAttribute的一个实现,它根据指定的绑定器类型,返回其实例。MVC在查找适当的绑定器时,遵循以下顺序:
在参数上通过ModelBinderAttribute特性指定的绑定器在ModelBinders.Binders中注册的绑定器在类型上通过ModelBinderAttribute特性指定的绑定器默认绑定器(通常为DefaultModelBinder)
BindAttribute特性用于指定默认绑定器在绑定时的具体行为:Exlude属性指定绑定器在填充数据时,不处理的属性列表,Include属性指定绑定器在填充数据时,只需处理的属性列表,Prefix用于指定类型的前缀,默认情况下,前缀为控制器方法中参数的名称。IsPropertyAllowed方法用于判断特定的属性是否需要被处理。自定义值提供器要实现自定义的值提供器,我们需要实现一个ValueProviderFactory和一个IValueProvider,之后将其添加到ValueProviderFactories.Factories集合中。下例实现一个CookieValueProvider,用于从Cookie中获取数据:
1、创建一个空MVC项目2、实现CookieValueProviderFactory工厂及CookieValueProvider提供器显示行号 复制代码 ? CookieValueProviderFactory
public class CookieValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
   {
return new CookieValueProvider(controllerContext.HttpContext.Request.Cookies);
}
private class CookieValueProvider : IValueProvider
   {
private HttpCookieCollection Cookies;
public CookieValueProvider(HttpCookieCollection cs)
       {
Cookies = cs;
    }
public bool ContainsPrefix(string prefix)
       {
return Cookies.AllKeys.Contains(prefix);
    }
public ValueProviderResult GetValue(string key)
       {
if (!ContainsPrefix(key))
return null;
string value = Cookies[key].Value;
return new ValueProviderResult(value,value, CultureInfo.CurrentCulture );
    }
}
}
// 3、在Application_Start中注册自定义值提供器工厂
ValueProviderFactories.Factories.Insert(0, new CookieValueProviderFactory());
4、实现一个测试控制器,HomeController:显示行号 复制代码 ? HomeController
public class HomeController : Controller
{
public ActionResult Index(DateTime? lastTime)
   {
if (!Request.Cookies.AllKeys.Contains("lastTime"))
       {
Response.Cookies.Add(new HttpCookie("lastTime", DateTime.Now.ToString()));
    }
return Content(lastTime == null ? String.Empty : lastTime.ToString());
}
}
// 注意,参数类型为DateTime?可空类型,这样可防止默认绑定器在没有找到对应值时报错。测试时,首次运行,由于Cookie中没有lastTime项,所以页面为空,刷新一次页面,此时页面将显示上一次访问的时间。
自定义模型绑定器通过实现IModelBinder接口可实现自定义模型绑定器,下例将实现一个XDocument类型的绑定器:
1、创建一个空MVC项目2、实现XDocumentBinder显示行号 复制代码 ? XDocumentBinder
public class XDocumentBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   {
string key = bindingContext.ModelName;
ValueProviderResult vpr = bindingContext.ValueProvider.GetValue(key);
if (vpr !=null && vpr.RawValue != null && !String.IsNullOrEmpty(vpr.AttemptedValue))
       {
//在模型状态中保存尝试值
bindingContext.ModelState.SetModelValue(key, vpr);
string tempString = ((String[])vpr.RawValue)[0];
XDocument xml = null;
try
           {
xml = XDocument.Parse(tempString);
        }
catch (XmlException)
           {
//无法解析XML文本,则设置模型错误状态
bindingContext.ModelState.AddModelError(key, "Not valid XML");
return null;
        }
//如果模型已经存在,则替换
XDocument existingModel = bindingContext.Model as XDocument;
if (existingModel != null)
           {
if (existingModel.Root != null)
               {
existingModel.Root.ReplaceWith(xml.Root);
            }
else
               {
existingModel.Add(xml.Root);
            }
return existingModel;
        }
else
           {
return xml;
        }
    }
return null;
}
}
// 3、在Application_Start中注册XDocumentBinder
ModelBinders.Binders.Add(typeof(XDocument), new XDocumentBinder());
4、创建用于测试的HomeController及其View显示行号 复制代码 ? HomeController
public class HomeController : Controller
{
[ValidateInput(false)]
public ActionResult Index(XDocument xml)
   {
        if (xml == null)
       {
            return View();
    }
        else
       {
            Response.Clear();
            return Content(xml.ToString(), "application/xml");
    }
}
}
// 显示行号 复制代码 ? Index.aspx
    <div>
    <%using (Html.BeginForm())
      {%>
      <%= Html.ValidationSummary() %>
       <%=Html.TextArea("xml", new { Rows=20, Cols=50 })%>
       <input type="submit" value ="submit" />
     <% } %>
    </div>
// 
关于注册XDocumentBinder,我们可以通过ModelBinders.Binders将某个类型映射到绑定器,也可以通过在参数上、类型上通过ModelBinderAttribute特性来指定。本例中Index方法上,我们指定了[ValidateInput(false)]特性,用于跳过输入验证,这是因为默认设置下,MVC不允许提交XML类型的数据。默认绑定器扩展DefaultModelBinder默认绑定器通过Activator.CreateInstance来实例化模型类型,所以我们的模型必须要用无参构造器。要解除此限制,我们可以使用DI技术对默认绑定器进行扩展:从DefaultModelBinder继承一个新类,并重写CreateModel方法,使用DI来实例化模型。最后通过 ModelBinders.Binders.DefaultBinder属性指定我们的自定义默认绑定器。关于DI,在MVC中的扩展点(三)控制器工厂中有所涉及,有兴趣的朋友可参照该示例来实现自定义默认绑定器。源代码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: