Asp.net MVC源码分析--获取ModelBinder的优先级

来源:未知 责任编辑:智问网络 发表时间:2013-10-22 19:24 点击:

 

在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上面设置断点,看一下代码执行的情况,就可以清楚的看到我们的结论是多么的无比正确.:)

 

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

谢谢大家。


 

摘自 十一月的雨

    发表评论
    请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
    用户名: 验证码:点击我更换图片
    最新评论 更多>>

    推荐热点

    • 浅析.NET下XML数据访问新机制
    • asp.net 面试+笔试题目第1/2页
    • C# 邮件地址是否合法的验证
    • asp.net 设置GridView的选中行的实现代码
    • C#高级编程:数据库连接[1]
    • 经典C++程序1
    • IIS 自动回收导致后台定时器失效的问题解决
    • ASP.NET&#160;GridView列表代码示例
    • Asp.net MVC源码分析--Action Filter的链式调用
    网站首页 - 友情链接 - 网站地图 - TAG标签 - RSS订阅 - 内容搜索
    Copyright © 2008-2015 计算机技术学习交流网. 版权所有

    豫ICP备11007008号-1