zoukankan      html  css  js  c++  java
  • asp.net mvc源码分析-Action篇 IModelBinder

    我们首先还是看看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();

            }
       }运行结果如图:

     

     
  • 相关阅读:
    python ModuleNotFoundError: No module named 'requests' 的 解决方案
    Win环境下如何在cmd运行python文件
    阿里云ECS服务器连接RDS数据库
    mysql5.6采集数据插入出现MySQL server has gone away解决办法
    Ubuntu 18.04 单系统U盘安装
    查看ubuntu系统是32位还是64位,查看系统版本
    Ubuntu 18.04 设置固定的静态ip
    Ubuntu 18.04 新系统 允许root远程登录设置方法
    ubuntu 新系统 使用root用户登录
    Ubuntu 18.04远程登录服务器--ssh的安装和配置
  • 原文地址:https://www.cnblogs.com/rrxc/p/4108119.html
Copyright © 2011-2022 走看看