ASP.NET MVC5学习笔记之Action参数模型绑定基本过程

  当我们在Controller中定义一个Action,通常会定义一个或多个参数,每个参数称为一个模型,ASP.NET MVC框架提供了一种机制称为模型绑定,会尝试自动从请求的信息中实例化每一个模型并赋值。这其中又涉及模型的元数据提供和模型的验证。

  我们不妨试想一下,如果来定义一种从字符串值到对象值的映射机制,可能要知道以下信息:

1. 对象的类型和名称等对象本身的元数据

2. 对象属性的元数据信息

3. 查询字符串到对象和对象属性的值映射机制

4. 具体的绑定过程

  前面的1,2 由ASP.NET MVC框架的元数据提供机制保证,3由其值提供机制保证,4由具体的模型绑定对象执行。只不过ASP.NET MVC在做完模型绑定后,顺带验证了模型的验证。

  现在我们来看看具体的代码.参数模型绑定执行肯定在具体的Action方法执行之前,在ControllerActionInvoker的InvokeAction方中,执行Action方法是InvokeActionMethodWithFilters,在其前一行代码是IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor); 这个方法的目的把Action的所有参数值根据参数名封装到一个字典里。现在来看看其实现:

 1 protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
 2         {
 3             Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
 4             ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters();
 5 
 6             foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors)
 7             {
 8                 parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor);
 9             }
10             return parametersDict;
11         }

  可以看到通过ActionDescriptor获取所有的参数描述信息数组,每个参数通过调用GetParameterValue获取其参数值。这里有一个重要的类ParameterDescriptor,该类是个抽象类其定义如下:

 1  public abstract class ParameterDescriptor : ICustomAttributeProvider
 2     {
 3        
 4         protected ParameterDescriptor();
 5         public abstract ActionDescriptor ActionDescriptor { get; }
 6 
 7         public virtual ParameterBindingInfo BindingInfo { get; }
 8 
 9         public virtual object DefaultValue { get; }
10         public abstract string ParameterName { get; }
11         public abstract Type ParameterType { get; }
12         public virtual object[] GetCustomAttributes(bool inherit);
13         public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);
14         public virtual bool IsDefined(Type attributeType, bool inherit);
15     }

 这个类型除了BindingInfo属性其它属性没什么好说的,BindingInfo的类型是ParameterBindingInfo,描述该参数类型模型绑定过程中将使用的信息,具体那些信息我们来看这个类型的定义:

1  public abstract class ParameterBindingInfo
2     {
3         protected ParameterBindingInfo();
4         public virtual IModelBinder Binder { get; }
5         public virtual ICollection<string> Exclude { get; }
6         public virtual ICollection<string> Include { get; }
7         public virtual string Prefix { get; }
8     }

Binder属性是指参数特定的绑定器,通过ModelBinderAttribute来指定, Exclude和Include表示不参与或参与参数属性模型绑定列表,Prefix 表示绑定过程中使用的前缀,这三个属性是通过BindAttribute属性来设置的。

  现在回过头来看看GetParameterValue 方法的实现:

 1 protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
 2         {
 3             // collect all of the necessary binding properties
 4             Type parameterType = parameterDescriptor.ParameterType;
 5             IModelBinder binder = GetModelBinder(parameterDescriptor);
 6             IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
 7             string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
 8             Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);
 9 
10             // finally, call into the binder
11             ModelBindingContext bindingContext = new ModelBindingContext()
12             {
13                 FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified
14                 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
15                 ModelName = parameterName,
16                 ModelState = controllerContext.Controller.ViewData.ModelState,
17                 PropertyFilter = propertyFilter,
18                 ValueProvider = valueProvider
19             };
20 
21             object result = binder.BindModel(controllerContext, bindingContext);
22             return result ?? parameterDescriptor.DefaultValue;
23         }

   a. 前面的几行代码准备绑定上下文使用的信息,简单的这里不说了,具体的ModelBinder通过GetModelBinder方法获取,它的实现是这样的:

  return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType); 

  我们可以知道,通过ModelBinderAttribute指定的ModelBinder具有最高的优先级,Binders是ModelBinders.Binders静态属性,如果参数未指定ModelBinder,则通过ModelBinders.Binders.ModelBinders.Binders.GetBinder方法查找,具体查找这里不分析了,这里给出一个结果:

  1. 在参数上应用了ModelBinderAttribute

  2. 在ModelBinderProvider集合中查找

  3. 在全局注册的模型绑定集合表中查找

  4. 在参数类型上通过CustomModelBinderAttribute指定的模型绑定类型

  5. 返回默认的DefaultModelBinder

 b. 实例化绑定上文下,我们看到如果我们通过BinderAttribute的Prefix指定了前缀,系统默认的FallbackToEmptyPrefix将不再使用, 另外参数模型的元数据通过调用系统的元数据提供机制ModelMetadataProviders.Current.GetMetadataForType.

  c. 执行模型绑定,如果返回null再返回参数指定的默认值

   这段代码信息量比较大,包含了元数据提供机制,值提供机制,模型绑定与验证。后面的章节将分别描述。

posted @ 2014-05-14 17:55  十三  阅读(2078)  评论(0编辑  收藏  举报