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再返回参数指定的默认值

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

你可能感兴趣的:(asp.net)