asp.net mvc源码分析-Action篇 IModelBinder

紧接着上篇asp.net mvc源码分析-Action篇 ParameterDescriptor 文章 在ReflectedParameterBindingInfo有这个public override IModelBinder Binder属性,同时在ControllerActionInvoker中也有一个类似的 protected internal ModelBinderDictionary Binders  看见这两个属性名称我们就应该知道ModelBinderDictionary是IModelBinder的一个集合类,public class ModelBinderDictionary : IDictionary<Type, 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。

其 方法如下:

  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类怎么做。

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();

        }
   }
运行结果如图:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值