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

posted @ 2011-11-22 17:14  十一月的雨  阅读(1757)  评论(1编辑  收藏  举报