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

你可能感兴趣的:(asp.net)