当我们在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的所有参数值根据参数名封装到一个字典里。现在来看看其实现:

 protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters(); foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors)
{
parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor);
}
return parametersDict;
}

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

  public abstract class ParameterDescriptor : ICustomAttributeProvider
{ protected ParameterDescriptor();
public abstract ActionDescriptor ActionDescriptor { get; } public virtual ParameterBindingInfo BindingInfo { get; } public virtual object DefaultValue { get; }
public abstract string ParameterName { get; }
public abstract Type ParameterType { get; }
public virtual object[] GetCustomAttributes(bool inherit);
public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);
public virtual bool IsDefined(Type attributeType, bool inherit);
}

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

  public abstract class ParameterBindingInfo
{
protected ParameterBindingInfo();
public virtual IModelBinder Binder { get; }
public virtual ICollection<string> Exclude { get; }
public virtual ICollection<string> Include { get; }
public virtual string Prefix { get; }
}

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

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

 protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
{
// collect all of the necessary binding properties
Type parameterType = parameterDescriptor.ParameterType;
IModelBinder binder = GetModelBinder(parameterDescriptor);
IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor); // finally, call into the binder
ModelBindingContext bindingContext = new ModelBindingContext()
{
FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
ModelName = parameterName,
ModelState = controllerContext.Controller.ViewData.ModelState,
PropertyFilter = propertyFilter,
ValueProvider = valueProvider
}; object result = binder.BindModel(controllerContext, bindingContext);
return result ?? parameterDescriptor.DefaultValue;
}

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 MVC5学习笔记之Action参数模型绑定基本过程的更多相关文章

  1. ASP.NET MVC5学习笔记之Action参数模型绑定之模型元数据和元数据提供

    一. 元数据描述类型ModelMetadata 模型元数据是对Model的描述信息,在ASP.NET MVC框架中有非常重要的作用,在模型绑定,模型验证,模型呈现等许多地方都有它的身影.描述Model ...

  2. ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系

    这一节我们关注模型绑定的值提供体系,先来介绍几个重要的接口 一. IValueProvider,接口定义如下: public interface IValueProvider { bool Conta ...

  3. ASP.NET MVC5学习笔记01

    由于之前在项目中也使用MVC进行开发,但是具体是那个版本就不是很清楚了,但是我觉得大体的思想是相同的,只是版本高的在版本低的基础上增加了一些更加方便操作的东西.下面是我学习ASP.NET MVC5高级 ...

  4. ASP.NET MVC5学习笔记之Filter提供体系

    前面我们介绍了Filter的基本使用,但各种Filter要在合适的时机运行起来,需要预先准备好,现在看看ASP.NET MVC框架是怎么做的. 一.Filter集合 在ControlerActionI ...

  5. ASP.NET MVC5学习笔记之Controller同步执行架构分析

    在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码.在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成C ...

  6. ASP.NET MVC5 学习笔记-1 控制器、路由、返回类型、选择器、过滤器

    [TOC] 1. Action 1.1 新建项目 新建项目->Web->Asp.net Web应用程序,选择MVC,选择添加测试. 在解决方案上右键,选择"管理NuGet程序包& ...

  7. ASP.NET MVC5学习笔记之Filter基本介绍

    Filter是ASP.NET MVC框架提供的基于AOP(面向方面)设计,提供在Action执行前后做一些非业务逻辑通用处理,如用户验证,缓存等.现在来看看Filter相关的一些类型信息. 一.基本类 ...

  8. ASP.NET MVC5 学习笔记-2 Razor

    1. Razor @*注释*@ 你在用 @Request.Browser.Browser, 发送邮件给support@qq.com, 转义@@qq @{ var amounts = new List& ...

  9. ASP.NET MVC5 学习笔记-3 Model

    1. Model 1.1 添加一个模型 注意,添加属性时可以输入"prop",会自动输入代码段. public class CheckoutAccount { public int ...

随机推荐

  1. 字符串属性使用strong的原因

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  2. (转)直接拿来用!最火的iOS开源项目(二)

    “每一次的改变总意味着新的开始.”这句话用在iOS上可谓是再合适不过的了.GitHub上的iOS开源项目数不胜数,iOS每一次的改变,总会引发iOS开源项目的演变,从iOS 1.x到如今的iOS 7, ...

  3. 使用升级版的 Bootstrap typeahead v1.2.2

    上次介绍了 Bootstrap 2 中附带的 typeahead,功能强大,但是使用起来不太方便,作者 Terry Rosen 已经升级了一个新版本 v1.2.2,作出了很大的改进. 下载地址 htt ...

  4. http是什么?

    http HyperText Transfer Protocol 超文本传输协议,是一个应用层通信协议. 可以用wireshark抓取.

  5. SQL Server :Stored procedures存储过程初级篇

    对于SQL Server,我是个拿来主义.很多底层的原理并不了解,就直接模仿拿着来用了,到了报错的时候,才去找原因进而逐步深入底层.我想,是每一次的报错,逼着我一点点进步的吧. 近期由于项目的原因,我 ...

  6. Yii 中比较常用的rules验证规则记录

    查看代码   打印 01 return array( 02   03     //必须填写 04     array('email, username, password,agree,verifyPa ...

  7. VR就是下一个浪潮_2016 (GMGC) 全球移动游戏大会观后感

    "VR就是下一个浪潮"  --2016 (GMGC) 全球移动游戏大会观后感   早在2014年参会Unity举办的一年一度的金立方盛典大会,就初次体验了VR头盔设备,于是印象深刻 ...

  8. Android开发-API指南-服务

    Service 英文原文:http://developer.android.com/guide/components/services.html 采集(更新)日期:2014-12-23 原博客:htt ...

  9. 断言--NSAssert

    NSAssert()是一个宏,用于开发阶段调试程序中的Bug,通过为NSAssert()传递条件表达式来断定是否属于Bug,满足条件返回真值,程序继续运行,如果返回假值,则抛出异常,并切可以自定义异常 ...

  10. Xcode清缓存

    前往-->按住option键进入资源库-->Developer-->Xcode-->DerivedData   删除里面的文件就行了