博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Asp.net MVC源码分析--获取ModelBinder的优先级
阅读量:4610 次
发布时间:2019-06-09

本文共 9038 字,大约阅读时间需要 30 分钟。

在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
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 plainprivate ParameterDescriptor[] LazilyFetchParametersCollection() {          return DescriptorUtil.LazilyFetchOrCreateDescriptors
( 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
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
errorMessageAccessor) { AttributeCollection allAttrs = TypeDescriptorHelper.Get(type).GetAttributes(); CustomModelBinderAttribute[] filteredAttrs = allAttrs.OfType
().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 FormTestModelBinderProviderImpl2());  //第三优先:System.Web.Mvc.ModelBinders.Binders[typeof(FormTestModel)] = new FormTestModelBinderImpl3(); 
//第四优先: [ModelBinder(typeof(FormTestModelBinderImpl4))]      public class FormTestModel      {  //codes  }

  

 

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

 

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

谢谢大家。

后记:

其它的相关文章:

MVC运行机制之源码剖析h

 

转载请注明出处: 

本文作者: 十一月的雨 

转载于:https://www.cnblogs.com/RobbinHan/archive/2011/11/22/2258964.html

你可能感兴趣的文章
mysql基础数据类型
查看>>
《windows程序设计》打印一页文本(06)
查看>>
3-05函数-关键参数
查看>>
实现jQuery扩展总结
查看>>
ssh免密钥登录一例问题
查看>>
[转]Android安装卸载Apk的接口
查看>>
Shell父进程获取子进程的变量值
查看>>
Sublime Text 2/3中Autoprefixer失效解决方法
查看>>
Python 通过lxml 解析html页面自动组合xpath实例
查看>>
Linux0.11内核之旅3——main.c(1)
查看>>
bWAPP练习--injection篇之HTML Injection - Reflected (POST)
查看>>
图实践经典问题一览
查看>>
Java将List<T>集合组装成树(Tree)树结构组装
查看>>
javaEE 之JSon
查看>>
Python多进程操作同一个文件,文件锁问题
查看>>
BOM——检测浏览器
查看>>
JavaScript 进阶(一)JS的"多线程"
查看>>
zabbix3.2 install
查看>>
Hanoi塔问题——递归
查看>>
c++11之函数式编程实例
查看>>