我们首先还是看看ReflectedParameterBindingInfo的Binder属性吧:

public override IModelBinder Binder {
            get {
                IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo,
                    () => String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes,
                        _parameterInfo.Name, _parameterInfo.Member));

return binder;
            }
        }

在ModelBinders中有一个属性public static ModelBinderDictionary Binders,这个binders内容如下

ModelBinderDictionary binders =new ModelBinderDictionary(){
                { typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder()},
                { typeof(byte[]), new ByteArrayModelBinder()},
                { typeof(Binary), new LinqBinaryModelBinder()}
            };
           说明默认的情况下就提供者3个Binder,但是这个属性是共有静态的,所以我们可以往里面添加自己的binder类,和前面文章讲的Filiter以及后面要讲的ValueProvider一样,

如在 Application_Start()方法中 ModelBinders.Binders.Add(xxx,xxxx)很不是很方便扩展了。

ModelBinders的GetBinderFromAttributes这个方法一看我们就能猜到它的逻辑了,

CustomModelBinderAttribute[] attrs = (CustomModelBinderAttribute[])element.GetCustomAttributes(typeof(CustomModelBinderAttribute), true /* inherit */);

获取 当前参数的CustomModelBinderAttribute特性,如果有该特性就调用第一个特性的GetBinder()方法并返回其值,没有特性则返回null,如果有多个特性则抛出异常,说明一个参数上是不可以有多个CustomModelBinderAttribute特性的,正样 ReflectedParameterBindingInfo的binder属性就设置好了。

下面 该轮到ControllerActionInvoker的Binders属性,

protected internal ModelBinderDictionary Binders {
            get {
                if (_binders == null) {
                    _binders = ModelBinders.Binders;
                }
                return _binders;
            }
            set {
                _binders = value;
            }
        }

可以 看到默认他返回的是ModelBinders.Binders。

接下来看看  IModelBinder binder = GetModelBinder(parameterDescriptor)这句究竟怎么返回的binder,

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

太简单了 ,首先看看参数是否有binder特性,如果有就返回相应的binder,否者根据参数类型获取对应的binder。

其 方法如下:

[csharp]
public IModelBinder GetBinder(Type modelType) { 
          return GetBinder(modelType, true /* fallbackToDefault */); 
      } 
 
      public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault) { 
          if (modelType == null) { 
              throw new ArgumentNullException("modelType"); 
          } 
 
          return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null); 
      } 
 
      private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) { 
 
          // Try to look up a binder for this type. We use this order of precedence:  
          // 1. Binder returned from provider  
          // 2. Binder registered in the global table  
          // 3. Binder attribute defined on the type  
          // 4. Supplied fallback binder  
 
          IModelBinder binder = _modelBinderProviders.GetBinder(modelType); 
          if (binder != null) { 
              return binder; 
          } 
 
          if (_innerDictionary.TryGetValue(modelType, out binder)) { 
              return binder; 
          } 
 
          binder = ModelBinders.GetBinderFromAttributes(modelType, 
              () => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName)); 
 
          return binder ?? fallbackBinder; 
      }

public IModelBinder GetBinder(Type modelType) {
            return GetBinder(modelType, true /* fallbackToDefault */);
        }

public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault) {
            if (modelType == null) {
                throw new ArgumentNullException("modelType");
            }

return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null);
        }

private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) {

// Try to look up a binder for this type. We use this order of precedence:
            // 1. Binder returned from provider
            // 2. Binder registered in the global table
            // 3. Binder attribute defined on the type
            // 4. Supplied fallback binder

IModelBinder binder = _modelBinderProviders.GetBinder(modelType);
            if (binder != null) {
                return binder;
            }

if (_innerDictionary.TryGetValue(modelType, out binder)) {
                return binder;
            }

binder = ModelBinders.GetBinderFromAttributes(modelType,
                () => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName));

return binder ?? fallbackBinder;
        }
这里需要注意binder选着的优先顺序,(1)从_modelBinderProviders里面找相应的binder

private ModelBinderProviderCollection _modelBinderProviders;
        public ModelBinderDictionary()
            : this(ModelBinderProviders.BinderProviders) {
        }
        internal ModelBinderDictionary(ModelBinderProviderCollection modelBinderProviders) {
            _modelBinderProviders = modelBinderProviders;
        }

public static class ModelBinderProviders {
        private readonly static ModelBinderProviderCollection _binderProviders = new ModelBinderProviderCollection {
        };
        public static ModelBinderProviderCollection BinderProviders {
            get {
                return _binderProviders;
            }
        }
    }

从这些代码我们可以得知 默认情况下_modelBinderProviders里面是没有数据 ,那么什么时候这个集合有数据了,当我们在Application_Start()中调用ModelBinderProviders.BinderProviders.Add(xxx)就有数据了,

(2)从_innerDictionary中取数据,这个数据时什么时候添加上去的了,看了ModelBinderDictionary的add放就明白了

public void Add(Type key, IModelBinder value) {
            _innerDictionary.Add(key, value);
        }

其实 ModelBinderDictionary内部很多方法都是围绕着_innerDictionary集合操作的。

(3)从参数数据类型上获取binder

(4)返货默认的DefaultBinder,该属性默认= new DefaultModelBinder()

现在 我们可以总结一下binder的优先顺序(1)参数上的CustomModelBinderAttribute特性;(2)ModelBinderProviders.BinderProviders.Add(xxx)注册的IModelBinderProvider;(3)ModelBinders的Binders;(4)参数类型上的CustomModelBinderAttribute特性;(5)返回默认的DefaultModelBinder

IValueProvider valueProvider = controllerContext.Controller.ValueProvider; 这句的讲解我们放到后面的文章中吧,

string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;这句获取参数名称,

Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);这个就只控制该参数时候需要绑定对应的值。

private static Predicate<string> GetPropertyFilter(ParameterDescriptor parameterDescriptor) {
            ParameterBindingInfo bindingInfo = parameterDescriptor.BindingInfo;
            return propertyName => BindAttribute.IsPropertyAllowed(propertyName, bindingInfo.Include.ToArray(), bindingInfo.Exclude.ToArray());
        }

BindAttribute.IsPropertyAllowed如下:

internal static bool IsPropertyAllowed(string propertyName, string[] includeProperties, string[] excludeProperties) {
            // We allow a property to be bound if its both in the include list AND not in the exclude list.
            // An empty include list implies all properties are allowed.
            // An empty exclude list implies no properties are disallowed.
            bool includeProperty = (includeProperties == null) || (includeProperties.Length == 0) || includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
            bool excludeProperty = (excludeProperties != null) && excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
            return includeProperty && !excludeProperty;
        }

现在 终于看到了BindAttribute在申明地方用到了。

现在 我们来做一个自定的ModelBinder类怎么做。

[csharp]
public class UserInfo 
    { 
        public string Name { set; get; } 
        public string Age { set; get; } 
    } 
    public class UserInfoModelBinder : IModelBinder 
    { 
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
        { 
            object obj = Activator.CreateInstance(bindingContext.ModelType); 
            foreach (PropertyInfo p in bindingContext.ModelType.GetProperties()) 
            { 
               ValueProviderResult vpResult=  bindingContext.ValueProvider.GetValue(p.Name); 
               if (vpResult != null) 
               { 
                   object value = vpResult.ConvertTo(p.PropertyType); 
                   p.SetValue(obj, value, null); 
               } 
            } 
            return obj; 
        } 
    } 
    public class HomeController : Controller 
    { 
 
        public ActionResult Index([ModelBinder(typeof(UserInfoModelBinder))]UserInfo userInfo) 
        { 
            return Content("Name:" + userInfo.Name + " Age:" + userInfo.Age); 
          //  return View();  
 
        } 
   }

public class UserInfo
    {
        public string Name { set; get; }
        public string Age { set; get; }
    }
    public class UserInfoModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            object obj = Activator.CreateInstance(bindingContext.ModelType);
            foreach (PropertyInfo p in bindingContext.ModelType.GetProperties())
            {
               ValueProviderResult vpResult=  bindingContext.ValueProvider.GetValue(p.Name);
               if (vpResult != null)
               {
                   object value = vpResult.ConvertTo(p.PropertyType);
                   p.SetValue(obj, value, null);
               }
            }
            return obj;
        }
    }
    public class HomeController : Controller
    {

public ActionResult Index([ModelBinder(typeof(UserInfoModelBinder))]UserInfo userInfo)
        {
            return Content("Name:" + userInfo.Name + " Age:" + userInfo.Age);
          //  return View();

}
   }运行结果如图:

 

asp.net mvc源码分析-Action篇 IModelBinder的更多相关文章

  1. ASP.NET MVC 源码分析(一)

    ASP.NET MVC 源码分析(一) 直接上图: 我们先来看Core的设计: 从项目结构来看,asp.net.mvc.core有以下目录: ActionConstraints:action限制相关 ...

  2. ASP.NET MVC源码分析

    MVC4 源码分析(Visual studio 2012/2013) HttpModule中重要的UrlRoutingModule 9:this.OnApplicationPostResolveReq ...

  3. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  4. asp.net mvc源码分析-ModelValidatorProviders 客户端的验证

    几年写过asp.net mvc源码分析-ModelValidatorProviders 当时主要是考虑mvc的流程对,客户端的验证也只是简单的提及了一下,现在我们来仔细看一下客户端的验证. 如图所示, ...

  5. MVC源码分析 - Action/Result 过滤器执行时机

    前面 的篇章, 解析了Action方法的查找, 以及 Authorize, Action, Result, Error 过滤器的加载时机. 也花了两篇去看授权和错误过滤器的使用. 但是对于 Actio ...

  6. MVC源码分析 - Action查找和过滤器的执行时机

    接着上一篇, 在创建好Controller之后, 有一个 this.ExecuteCore()方法, 这部分是执行的. 那么里面具体做了些什么呢? //ControllerBaseprotected ...

  7. asp.net MVC 源码分析

    先上一张图吧 asp.net请求机制的图  by传智播客邹华栋老师 然后是 邹老师添加MVC请求过程的图 其实MVC 是在.netframework上加了一个过滤器  HttpModule 在C:\W ...

  8. asp.net mvc源码分析-Route的GetRouteData

    我知道Route这里东西应该算路由,这里把它放到mvc里面有些不怎么合适,但是我想大家多数遇到路由都是在mvc的时候吧.首先我们还是来看看GetRouteData方法吧 [csharp] public ...

  9. MVC源码分析 - Action/Result 过滤器(续)

    上一篇 看到了Action/Result过滤器的执行顺序: OnActionExecuting -> Action -> OnActionExecuted -> OnResultEx ...

随机推荐

  1. JBOSS内存溢出处理

    JBOSS内存溢出处理 前几天公司一个项目的服务器坏了,就换了一个备份服务器顶替一下,但是没有跑一会就宕机了,一直报java.lang.OutOfMemoryError....一看到这里,就知道是内存 ...

  2. [Spring Boot 系列] 集成maven和Spring boot的profile功能

    由于项目的需要, 今天给spirng boot项目添加了profile功能.再网上搜索了一圈,也没有找到满意的参考资料,其实配置并不难,就是没有一个one stop(一站式)讲解的地方,所以有了写这篇 ...

  3. 纯HTML标签详解

    HTML标签很多,可是实际上常用的却就那么十几二十个,很多标签的功能渐渐的被大家忽略了.然后,如果在适当的时候,用一用,还是能在一定程序上 给我们的页面设计带来一点小小的方便的.下面这些HTML标签基 ...

  4. 快速获取Windows系统上的国家和地区信息

    Windows系统上包含了200多个国家和地区的数据,有时候编程需要这些资料.以下代码可以帮助你快速获取这些信息.将Console语句注释掉,可以更快的完成分析. static void Main(s ...

  5. git环境搭建

    Linux kernel  的官方 GIT地址是: http://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git 可以从这个地 ...

  6. Java C++ Python PHP JS等各种语言中的INT最值

    Java: Integer.MAX_VALUE; Integer.MIN_VALUE; C++ INT_MAX INT_MIN <limit.h> 有些其他头文件也有引用 Python & ...

  7. 线程中无法实例化spring注入的服务的解决办法

    问题描述 在Java Web应用中采用多线程处理数据,发现Spring注入的服务一直报NullPointerException.使用注解式的声明@Resource和XML配置的bean声明,都报空指针 ...

  8. 浅谈 Scala 中下划线的用途

    Scala 作为一门函数式编程语言,对习惯了指令式编程语言的同学来说,会不大习惯,这里除了思维方式之外,还有语法层面的,比如 underscore(下划线)就会出现在多种场合,令初学者相当疑惑,今天就 ...

  9. MYSQL数据库管理之权限管理

    经常遇到有网友在QQ群或者论坛上问关于mysql权限的问题,今天抽空总结一下关于这几年使用MYSQL的时候关于MYSQL数据库的权限管理的经验,也希望能对使用mysql的网友有所帮助! 一.MYSQL ...

  10. 一招解决OpenERP8.0安装旧版模块报错

    有喜欢尝鲜的网友开始玩8.0了,可是版本还没发布,社区的很多特别好的模块还没有升级到8,所以经常碰到模块无法安装的问题. No module name osv 网友提出将模块的 from osv im ...