值提供器 AND 模型绑定器

本章介绍了值提供器的作用,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类代码和自定义模型绑定器代码

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5.  
  6. using System.Web.Mvc; //IModelBinder
  7.  
  8. namespace MvcApplication1.Sample_ModelBinder
  9. {
  10.  
  11.     public class TestModelBinder : IModelBinder
  12.     {
  13.  
  14.         public object BindModel(ControllerContext controllerContext,
  15.         ModelBindingContext bindingContext)
  16.         {
  17.             var valueProvider = bindingContext.ValueProvider;
  18.           
  19.                 int x = (int)valueProvider.GetValue("X").ConvertTo(typeof(int));
  20.                 int y = (int)valueProvider.GetValue("Y").ConvertTo(typeof(int));
  21.  
  22.            
  23.            
  24.             return new Point(x, y);
  25.         }
  26.     }
  27.  
  28.  
  29.  
  30.     public class PointModelBinder : IModelBinder
  31.     {
  32.         //public object BindModel(ControllerContext controllerContext,
  33.         //ModelBindingContext bindingContext)
  34.         //{
  35.         //    var valueProvider = bindingContext.ValueProvider;
  36.         //    int x = (int)valueProvider.GetValue("X").ConvertTo(typeof(int));
  37.         //    int y = (int)valueProvider.GetValue("Y").ConvertTo(typeof(int));
  38.         //    return new Point(x, y);
  39.         //}
  40.  
  41.  
  42.         private TModel Get<TModel>(ControllerContext controllerContext,ModelBindingContext bindingContext,string name)
  43.         {
  44.             string fullName = name;
  45.             //1.modelname
  46.             if (!String.IsNullOrWhiteSpace(bindingContext.ModelName))
  47.                 fullName = bindingContext.ModelName + "." + name;
  48.  
  49.             //2.,valueProviderResult
  50.                 ValueProviderResult valueProviderResult =bindingContext.ValueProvider.GetValue(fullName);
  51.             //3.modelState
  52.                 ModelState modelState = new ModelState { Value = valueProviderResult };
  53.             //4.ModelstatebindingContext.ModelState
  54.                 bindingContext.ModelState.Add(fullName, modelState);
  55.             //5.null
  56.                 ModelMetadata metadata = bindingContext.PropertyMetadata[name];
  57.                 string attemptedValue = valueProviderResult.AttemptedValue;
  58.                 if (metadata.ConvertEmptyStringToNull&& String.IsNullOrWhiteSpace(attemptedValue))
  59.                     attemptedValue = null;
  60.  
  61.             //6.  
  62.                 TModel model;
  63.                 bool invalidValue = false;
  64.                 try
  65.                 {
  66.                     model = (TModel)valueProviderResult.ConvertTo(typeof(TModel));
  67.                     metadata.Model = model;                  //XX
  68.                 }
  69.                 catch (Exception)
  70.                 {
  71.                     model = default(TModel);             //int 0.boolfalse,null
  72.                     metadata.Model = attemptedValue;
  73.                     invalidValue = true;              //null
  74.                 }
  75.  
  76.  
  77.             ///
  78.                 IEnumerable<ModelValidator> validators =
  79.                 ModelValidatorProviders.Providers.GetValidators(
  80.                 metadata,
  81.                 controllerContext
  82.                 );
  83.             
  84.                 foreach (var validator in validators)
  85.                     foreach (var validatorResult in
  86.                     validator.Validate(bindingContext.Model))
  87.                         modelState.Errors.Add(validatorResult.Message);
  88.                 if (invalidValue && modelState.Errors.Count == 0)
  89.                     modelState.Errors.Add(
  90.                     String.Format(
  91.                     "The value '{0}' is not a valid value forr {1}.",
  92.                     attemptedValue,
  93.                     metadata.GetDisplayName()
  94.                     )
  95.                     );
  96.                 return model;
  97.         }
  98.         public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  99.         {
  100.  
  101.             //1.modelname object
  102.             //
  103.             if (!String.IsNullOrEmpty(bindingContext.ModelName) &&
  104.                 !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
  105.             {
  106.                 if (!bindingContext.FallbackToEmptyPrefix)
  107.                     return null;
  108.                 bindingContext = new ModelBindingContext
  109.                 {
  110.                     ModelMetadata = bindingContext.ModelMetadata,
  111.  
  112.                     ModelState = bindingContext.ModelState,
  113.                     PropertyFilter = bindingContext.PropertyFilter,
  114.                     ValueProvider = bindingContext.ValueProvider
  115.                 };
  116.             }
  117.  
  118.             bindingContext.ModelMetadata.Model = new Point();
  119.  
  120.             return new Point(Get<int>(controllerContext, bindingContext, "X"), Get<int>(controllerContext, bindingContext, "Y"));
  121.  
  122.         }
  123.  
  124.     }
  125.     [ModelBinder(typeof(PointModelBinder))]
  126.     //[ModelBinder(typeof(TestModelBinder))]
  127.     public class Point
  128.     {
  129.         private int x;
  130.  
  131.         public int X
  132.         {
  133.             get { return x; }
  134.           
  135.             set { x = value;}  //
  136.         }
  137.     
  138.         private int y;
  139.  
  140.     public int Y
  141.     {
  142.         get { return y;}
  143.         set { y = value;}
  144.     }
  145.     
  146.  
  147.  
  148.         public Point(int x, int y)
  149.         {
  150.             this.x = x;
  151.             this.y = y;
  152.         }
  153.         public Point()
  154.         {
  155.  
  156.         }
  157.     }
  158. }

通过调试可以发现ModelBindingContext  包含了

1)值提供器(字符串值提供器,form值提供器,routeDate值提供其,httpfilCollection提供器,childAction值提供器)

2)model 指明了是 point类

3)属性元数据 propertyMetadata  表明了有 x,y两属性

image

模型绑定器的原理

注明:元数据指的是属性的详细信息,比如:dispalyname特性,属性类型  等等

1. 值提供器中检索值,然后在模型状态中记录值

1)ModelBindingContext的值提供器,默认提供了5种值提供器

2)在模型状态中记录值是因为如果信息错误,可以将错误信息回传显示

2.获得一个描述该属性的模型元数据的副本,然后再决定用户输入值的内容

3.将值转换为目标类型,转换错误则赋值类型的默认值

4.记录错误默认错误信息,回传model

5.使用自定义模型版定器,在类型上面加上

[ModelBinder(typeof(PointModelBinder))]

原文地址:https://www.cnblogs.com/StudyLife/p/3628292.html