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

    运行结果如图:

  • 相关阅读:
    [SSRS] Use Enum values in filter expressions Dynamics 365 Finance and Operation
    Power shell deploy all SSRS report d365 FO
    display method in Dynamics 365 FO
    How To Debug Dynamics 365 Finance and Operation
    Computed columns and virtual fields in data entities Dynamics 365
    Azure DevOps for Power Platform Build Pipeline
    Create readonly entities that expose financial dimensions Dynamics 365
    Dataentity call stack dynamics 365
    Dynamics 365 FO extension
    Use singletenant servertoserver authentication PowerApps
  • 原文地址:https://www.cnblogs.com/majiang/p/2763721.html
Copyright © 2020-2023  润新知