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

第三优先:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值