zoukankan      html  css  js  c++  java
  • Asp.net MVC源码分析获取ModelBinder的优先级

    在asp.net mvc 框架中我们可以对System.Web.Mvc.Binders 进行扩展我们自定义的binder 类型,但是同时它还有一些其它的方法可以实现自定义的model binder.而且mvc在使用的时候还有一些策略,现分析如下:

    获取ModelBinder 对象的入口方法是GetParameterValue, 其中

    IModelBinder binder = GetModelBinder(parameterDescriptor);

    这一句代码决定了ModelBinder 的使用策略。 

     System.Web.Mvc.ControllerActionInvoker

    protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {  
               // collect all of the necessary binding properties  
               Type parameterType = parameterDescriptor.ParameterType;  
               //Hey 请过来获取binder 的入口在这里,csdn 代段没法加高亮,垃极啊~!  
               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;  
           }  
    
    private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) {  
              // look on the parameter itself, then look in the global table  
            return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);  
          }  
    

      

    这里优先从 parameterDescriptor.BindingInfo中得到IModelBinder, 接下来我们看一下怎么从parameterDescriptor中获取binder

    首先找到 ReflectedActionDescriptor对象,可以看到在GetParameters方法中生成了ReflectedParameterDescriptor 对象,

    System.Web.Mvc.ReflectedActionDescriptor

    public override ParameterDescriptor[] GetParameters() {  
             ParameterDescriptor[] parameters = LazilyFetchParametersCollection();  
      
             // need to clone array so that user modifications aren't accidentally stored  
             return (ParameterDescriptor[])parameters.Clone();  
         }  
    view plain
    private ParameterDescriptor[] LazilyFetchParametersCollection() {  
            return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>(  
                ref _parametersCache /* cacheLocation */,  
                MethodInfo.GetParameters /* initializer */,  
                parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */);  
        }  
    

      

    这个对象中调用了ModelBinders.GetBinderFromAttributes方法来获取binder

    ReflectedParameterDescriptor -> ReflectedParameterBindingInfo

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

    System.Web.Mvc.ModelBinders

    internal static IModelBinder GetBinderFromAttributes(ICustomAttributeProvider element, Func<string>  
     errorMessageAccessor) {   
    CustomModelBinderAttribute[] attrs = (CustomModelBinderAttribute[])element.  
    GetCustomAttributes(typeof(CustomModelBinderAttribute),   
    true /* inherit */);   
      
    return GetBinderFromAttributesImpl(attrs, errorMessageAccessor);  
     }  
    

      

    接下来我们看

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

    语句后面的的分支.

    System.Web.Mvc.ModelBinderDictionary  

    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;  
          }  
    
    internal static IModelBinder GetBinderFromAttributes(Type type, Func<string> errorMessageAccessor) {  
               AttributeCollection allAttrs = TypeDescriptorHelper.Get(type).GetAttributes();  
               CustomModelBinderAttribute[] filteredAttrs = allAttrs.OfType<CustomModelBinderAttribute>().ToArray();  
               return GetBinderFromAttributesImpl(filteredAttrs, errorMessageAccessor);  
           }  
    

      

    到这里我们就清楚的知道了获取IModelBinder的优先级。

    结论:

    1.如果在Action参数中的对象中标记了元数据的IModelBinder 实现时则调用该实现。//注:如果这句不理解可以继续看下面的实例

    2.如果在参数中没有标记元数据的IModelBinder 实现 ,则调用的优先顺序为:

     // (1).从全局的ModelBinderProviders.BinderProviders 中查找,我们也可以注册自己的provider
     // (2).从全局的System.Web.Mvc.Binders 对象中查找,自己也可以注册自己的binder
     // (3). 从参数对象的类型中的attribute 找到 ModelBinderAtribute中的IModelBinder 实现.
     // (4). 最后如果经过以上步骤都没有找到IModelBinder的实现的话,就用MVC默认的实现DefaultModelBinder类,

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

      

    示例代码:

    下面我们通过实例代码来印证以上的结论。

    //Model 类:
    public class FormTestModel  
        {  
            [Required]  
            [DataType(DataType.Text)]  
            [Display(Name = "Room Name")]  
            public string RoomName { get; set; }  
      
            [Required]  
            [DataType(DataType.Text)]  
            [Display(Name = "Room Count")]  
            public string RoomCount { get; set; }      
      
        }  
    
    //Action类:
    [HttpPost]  
           public ActionResult FormTest(FormTestModels model, string returnUrl)  
           {  
            <span style="white-space:pre">    </span>return view();  
           }  
    
    //我们新建四个IModelBinder 的实现;
    internal sealed class FormTestModelBinderImpl1 : IModelBinder  
        {  
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)  
            {  
                DefaultModelBinder defBinder = new DefaultModelBinder();  
                return defBinder.BindModel(controllerContext, bindingContext);  
            }  
        }  
        internal sealed class FormTestModelBinderImpl2 : IModelBinder  
        {  
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)  
            {  
                DefaultModelBinder defBinder = new DefaultModelBinder();  
                return defBinder.BindModel(controllerContext, bindingContext);  
            }  
        }  
        internal sealed class FormTestModelBinderImpl3 : IModelBinder  
        {  
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)  
            {  
                DefaultModelBinder defBinder = new DefaultModelBinder();  
                return defBinder.BindModel(controllerContext, bindingContext);  
            }  
        }  
        internal sealed class FormTestModelBinderImpl4 : IModelBinder  
        {  
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)  
            {  
                DefaultModelBinder defBinder = new DefaultModelBinder();  
                return defBinder.BindModel(controllerContext, bindingContext);  
            }  
        }     
      
        public sealed class FormTestModelBinderProviderImpl : IModelBinderProvider  
        {  
            public IModelBinder GetBinder(Type modelType)  
            {  
                if (modelType == typeof(FormTestModels))  
                {  
                    //"provider implementition";  
                    return new FormTestModelsModelBinderImpl2();  
                }  
      
                return null;  
            }  
        }  
    

     

    然后我们分别加入:
    //第一优先:
    [HttpPost]  
           public ActionResult FormTest([ModelBinder(typeof(FormTestModelBinderImpl1))] FormTestModel model, string returnUrl)  
           {  
             return view();  
           }  
    
    //第二优先:
    ModelBinderProviders.BinderProviders.Add(new FormTestModelBinderProviderImpl2());  
    //第三优先:
    System.Web.Mvc.ModelBinders.Binders[typeof(FormTestModel)] = new FormTestModelBinderImpl3();  
    //第四优先:
     [ModelBinder(typeof(FormTestModelBinderImpl4))]  
        public class FormTestModel  
        {  
    //codes  
    }  
    

      

     

    最后我们在四个binder上面设置断点,看一下代码执行的情况,就可以清楚的看到我们的结论是多么的无比正确.:)

    最后感谢大家观看,以上分析如有雷同纯属巧合。

    谢谢大家。



    后记:

    其它的相关文章:

    MVC运行机制之源码剖析http://blog.csdn.net/study_live/article/details/4871745

    转载请注明出处:http://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html 

    本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan

  • 相关阅读:
    hdu1421 搬寝室(dp)
    HDU 2577(DP)
    扩展欧几里德算法
    unique函数的作用
    区间更新 zoj3911
    set的应用
    vue 事件处理器
    vue Class与style绑定
    vue的计算属性
    sass入门
  • 原文地址:https://www.cnblogs.com/RobbinHan/p/2258964.html
Copyright © 2011-2022 走看看