本章介绍了值提供器的作用,ASP MVC自带的5中值提供器.以及模型绑定器的作用,自定义模型绑定器并使用自定义的模型绑定器(类型上加上[ModelBinder(typeof(xx))]或者在全局模型绑定器中注册)。
补充:全局模型绑定器中注册自定义模型绑定器
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(Point), new PointModelBinder());
}
值提供器
ASP .NET MVC 框架自带的若干值提供器可以提供以下数据源中的数据:
● 子操作(RenderAction)的显式值
● 表单值
● 来自 XMLHttpRequest 的 JSON 数据
● 路由值
● 查询字符串值
● 上传的文件
值提供器来自值提供器工厂,并且系统按照值提供器的注册顺序来从中搜寻数据(上面
的列表使用的是默认顺序,自顶而下)。开发人员可以编写自己的值提供器工厂和值提供器,
并且还可以将它们插入到包含在 ValueProviderFactories.Factories 中的工厂列表中。当在模
型绑定期间需要使用额外的数据源时,开发人员通常选择编写自己的值提供器工厂和值提
供器。
除了 ASP .NET MVC 本身包含的值提供器工厂以外,开发团队也在 ASP.NET MVC 3
Futures 包中包含了一些提供器工厂和值提供器,可从 http://aspnet.codeplex.com/releases/view/
58781 上下载,或者安装 NuGet 包 Mvc3Futures。具体包括以下提供器:
● Cookie 值提供器
● 服务器变量值提供器
● Session 值提供器
● TempData 值提供器
模型绑定器
1.作用
模型扩展的另一部分是模型绑定器。它们从值提供器系统中获取值,并利用获取的值创建新模型或者填充已有的模型。
模型绑定器,从值提供器中获取值。
ASP .NET MVC 中的默认模型绑定器( 为方便起见,命名为DefaultModelBinder)是一段功能非常强大的代码,它可以对传统类、集合类、列表、数组 甚至字典进行模型绑定。但是不支持对不改变对象的模型绑定(不可变指的是只能通过构造函数初始化值的类),如果遇见不可变的对象或者类型则默认的模型绑定器就无用了。此时需要自定义模型绑定器
2. 自定义模型绑定器
实现IModelBinder接口的 BinderModel方法
object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
主要是用到ModelBindingContext类
假设要对Point类进行模型绑定(Point类只能通过构造函数进行初始化,所以只能通过自定义模型提供器)
下面是Point类代码和自定义模型绑定器代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc; //IModelBinder
- namespace MvcApplication1.Sample_ModelBinder
- {
- public class TestModelBinder : IModelBinder
- {
- public object BindModel(ControllerContext controllerContext,
- ModelBindingContext bindingContext)
- {
- var valueProvider = bindingContext.ValueProvider;
- int x = (int)valueProvider.GetValue("X").ConvertTo(typeof(int));
- int y = (int)valueProvider.GetValue("Y").ConvertTo(typeof(int));
- return new Point(x, y);
- }
- }
- public class PointModelBinder : IModelBinder
- {
- //public object BindModel(ControllerContext controllerContext,
- //ModelBindingContext bindingContext)
- //{
- // var valueProvider = bindingContext.ValueProvider;
- // int x = (int)valueProvider.GetValue("X").ConvertTo(typeof(int));
- // int y = (int)valueProvider.GetValue("Y").ConvertTo(typeof(int));
- // return new Point(x, y);
- //}
- private TModel Get<TModel>(ControllerContext controllerContext,ModelBindingContext bindingContext,string name)
- {
- string fullName = name;
- //1.modelname
- if (!String.IsNullOrWhiteSpace(bindingContext.ModelName))
- fullName = bindingContext.ModelName + "." + name;
- //2.,valueProviderResult
- ValueProviderResult valueProviderResult =bindingContext.ValueProvider.GetValue(fullName);
- //3.modelState
- ModelState modelState = new ModelState { Value = valueProviderResult };
- //4.ModelstatebindingContext.ModelState
- bindingContext.ModelState.Add(fullName, modelState);
- //5.null
- ModelMetadata metadata = bindingContext.PropertyMetadata[name];
- string attemptedValue = valueProviderResult.AttemptedValue;
- if (metadata.ConvertEmptyStringToNull&& String.IsNullOrWhiteSpace(attemptedValue))
- attemptedValue = null;
- //6.
- TModel model;
- bool invalidValue = false;
- try
- {
- model = (TModel)valueProviderResult.ConvertTo(typeof(TModel));
- metadata.Model = model; //XX
- }
- catch (Exception)
- {
- model = default(TModel); //int 0.boolfalse,null
- metadata.Model = attemptedValue;
- invalidValue = true; //null
- }
- ///
- IEnumerable<ModelValidator> validators =
- ModelValidatorProviders.Providers.GetValidators(
- metadata,
- controllerContext
- );
- foreach (var validator in validators)
- foreach (var validatorResult in
- validator.Validate(bindingContext.Model))
- modelState.Errors.Add(validatorResult.Message);
- if (invalidValue && modelState.Errors.Count == 0)
- modelState.Errors.Add(
- String.Format(
- "The value '{0}' is not a valid value forr {1}.",
- attemptedValue,
- metadata.GetDisplayName()
- )
- );
- return model;
- }
- public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
- {
- //1.modelname object
- //
- if (!String.IsNullOrEmpty(bindingContext.ModelName) &&
- !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
- {
- if (!bindingContext.FallbackToEmptyPrefix)
- return null;
- bindingContext = new ModelBindingContext
- {
- ModelMetadata = bindingContext.ModelMetadata,
- ModelState = bindingContext.ModelState,
- PropertyFilter = bindingContext.PropertyFilter,
- ValueProvider = bindingContext.ValueProvider
- };
- }
- bindingContext.ModelMetadata.Model = new Point();
- return new Point(Get<int>(controllerContext, bindingContext, "X"), Get<int>(controllerContext, bindingContext, "Y"));
- }
- }
- [ModelBinder(typeof(PointModelBinder))]
- //[ModelBinder(typeof(TestModelBinder))]
- public class Point
- {
- private int x;
- public int X
- {
- get { return x; }
- set { x = value;} //
- }
- private int y;
- public int Y
- {
- get { return y;}
- set { y = value;}
- }
- public Point(int x, int y)
- {
- this.x = x;
- this.y = y;
- }
- public Point()
- {
- }
- }
- }
通过调试可以发现ModelBindingContext 包含了
1)值提供器(字符串值提供器,form值提供器,routeDate值提供其,httpfilCollection提供器,childAction值提供器)
2)model 指明了是 point类
3)属性元数据 propertyMetadata 表明了有 x,y两属性
模型绑定器的原理
注明:元数据指的是属性的详细信息,比如:dispalyname特性,属性类型 等等
1. 值提供器中检索值,然后在模型状态中记录值
1)ModelBindingContext的值提供器,默认提供了5种值提供器
2)在模型状态中记录值是因为如果信息错误,可以将错误信息回传显示
2.获得一个描述该属性的模型元数据的副本,然后再决定用户输入值的内容
3.将值转换为目标类型,转换错误则赋值类型的默认值
4.记录错误默认错误信息,回传model
5.使用自定义模型版定器,在类型上面加上
[ModelBinder(typeof(PointModelBinder))]