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

[转载]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

01 protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {
02 // collect all of the necessary binding properties
03 Type parameterType = parameterDescriptor.ParameterType;
04 //Hey 请过来获取binder 的入口在这里,csdn 代段没法加高亮,垃极啊~!
05 IModelBinder binder = GetModelBinder(parameterDescriptor);
06 IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
07 string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
08 Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);
09
10 // finally, call into the binder
11 ModelBindingContext bindingContext = new ModelBindingContext() {
12 FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified
13 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
14 ModelName = parameterName,
15 ModelState = controllerContext.Controller.ViewData.ModelState,
16 PropertyFilter = propertyFilter,
17 ValueProvider = valueProvider
18 };
19
20 object result = binder.BindModel(controllerContext, bindingContext);
21 return result ?? parameterDescriptor.DefaultValue;
22 }
23
24 private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) {
25 // look on the parameter itself, then look in the global table
26 return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
27 }

这里优先从 parameterDescriptor.BindingInfo中得到IModelBinder, 接下来我们看一下怎么从parameterDescriptor中获取binder

首先找到 ReflectedActionDescriptor对象,可以看到在GetParameters方法中生成了ReflectedParameterDescriptor 对象,

System.Web.Mvc.ReflectedActionDescriptor

01 public override ParameterDescriptor[] GetParameters() {
02 ParameterDescriptor[] parameters = LazilyFetchParametersCollection();
03
04 // need to clone array so that user modifications aren't accidentally stored
05 return (ParameterDescriptor[])parameters.Clone();
06 }
07 view plain
08 private ParameterDescriptor[] LazilyFetchParametersCollection() {
09 return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>(
10 ref _parametersCache /* cacheLocation */,
11 MethodInfo.GetParameters /* initializer */,
12 parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */);
13 }

这个对象中调用了ModelBinders.GetBinderFromAttributes方法来获取binder

ReflectedParameterDescriptor -> ReflectedParameterBindingInfo

1 public override IModelBinder Binder {
2 get {
3 IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo,
4 () => String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes,
5 _parameterInfo.Name, _parameterInfo.Member));
6
7 return binder;
8 }
9 }

System.Web.Mvc.ModelBinders

1 internal static IModelBinder GetBinderFromAttributes(ICustomAttributeProvider element, Func<string>
2 errorMessageAccessor) {
3 CustomModelBinderAttribute[] attrs = (CustomModelBinderAttribute[])element.
4 GetCustomAttributes(typeof(CustomModelBinderAttribute),
5 true /* inherit */);
6
7 return GetBinderFromAttributesImpl(attrs, errorMessageAccessor);
8 }

接下来我们看

return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);

语句后面的的分支.

System.Web.Mvc.ModelBinderDictionary

01 private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) {
02
03 // Try to look up a binder for this type. We use this order of precedence:
04 // 1. Binder returned from provider
05 // 2. Binder registered in the global table
06 // 3. Binder attribute defined on the type
07 // 4. Supplied fallback binder
08
09 IModelBinder binder = _modelBinderProviders.GetBinder(modelType);
10 if (binder != null) {
11 return binder;
12 }
13
14 if (_innerDictionary.TryGetValue(modelType, out binder)) {
15 return binder;
16 }
17
18 binder = ModelBinders.GetBinderFromAttributes(modelType,
19 () => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName));
20
21 return binder ?? fallbackBinder;
22 }
23
24 internal static IModelBinder GetBinderFromAttributes(Type type, Func<string> errorMessageAccessor) {
25 AttributeCollection allAttrs = TypeDescriptorHelper.Get(type).GetAttributes();
26 CustomModelBinderAttribute[] filteredAttrs = allAttrs.OfType<CustomModelBinderAttribute>().ToArray();
27 return GetBinderFromAttributesImpl(filteredAttrs, errorMessageAccessor);
28 }

到这里我们就清楚的知道了获取IModelBinder的优先级。

结论:

1.如果在Action参数中的对象中标记了元数据的IModelBinder 实现时则调用该实现。//注:如果这句不理解可以继续看下面的实例

2.如果在参数中没有标记元数据的IModelBinder 实现 ,则调用的优先顺序为:

// (1).从全局的ModelBinderProviders.BinderProviders 中查找,我们也可以注册自己的provider
// (2).从全局的System.Web.Mvc.Binders 对象中查找,自己也可以注册自己的binder
// (3). 从参数对象的类型中的attribute 找到 ModelBinderAtribute中的IModelBinder 实现.
// (4). 最后如果经过以上步骤都没有找到IModelBinder的实现的话,就用MVC默认的实现DefaultModelBinder类,

1 public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault) {
2 if (modelType == null) {
3 throw new ArgumentNullException("modelType");
4 }
5
6 return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null);
7 }

示例代码:

下面我们通过实例代码来印证以上的结论。

01 //Model 类:
02 public class FormTestModel
03 {
04 [Required]
05 [DataType(DataType.Text)]
06 [Display(Name = "Room Name")]
07 public string RoomName { get; set; }
08
09 [Required]
10 [DataType(DataType.Text)]
11 [Display(Name = "Room Count")]
12 public string RoomCount { get; set; }
13
14 }
15
16 //Action类:
17 [HttpPost]
18 public ActionResult FormTest(FormTestModels model, string returnUrl)
19 {
20 <span style="white-space:pre">    </span>return view();
21 }
22
23 //我们新建四个IModelBinder 的实现;
24 internal sealed class FormTestModelBinderImpl1 : IModelBinder
25 {
26 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
27 {
28 DefaultModelBinder defBinder = new DefaultModelBinder();
29 return defBinder.BindModel(controllerContext, bindingContext);
30 }
31 }
32 internal sealed class FormTestModelBinderImpl2 : IModelBinder
33 {
34 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
35 {
36 DefaultModelBinder defBinder = new DefaultModelBinder();
37 return defBinder.BindModel(controllerContext, bindingContext);
38 }
39 }
40 internal sealed class FormTestModelBinderImpl3 : IModelBinder
41 {
42 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
43 {
44 DefaultModelBinder defBinder = new DefaultModelBinder();
45 return defBinder.BindModel(controllerContext, bindingContext);
46 }
47 }
48 internal sealed class FormTestModelBinderImpl4 : IModelBinder
49 {
50 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
51 {
52 DefaultModelBinder defBinder = new DefaultModelBinder();
53 return defBinder.BindModel(controllerContext, bindingContext);
54 }
55 }
56
57 public sealed class FormTestModelBinderProviderImpl : IModelBinderProvider
58 {
59 public IModelBinder GetBinder(Type modelType)
60 {
61 if (modelType == typeof(FormTestModels))
62 {
63 //"provider implementition";
64 return new FormTestModelsModelBinderImpl2();
65 }
66
67 return null;
68 }
69 }
01 然后我们分别加入:
02 //第一优先:
03 [HttpPost]
04 public ActionResult FormTest([ModelBinder(typeof(FormTestModelBinderImpl1))] FormTestModel model, string returnUrl)
05 {
06 return view();
07 }
08
09 //第二优先:
10 ModelBinderProviders.BinderProviders.Add(new FormTestModelBinderProviderImpl2());
11 //第三优先:
12 System.Web.Mvc.ModelBinders.Binders[typeof(FormTestModel)] = new FormTestModelBinderImpl3();
1 //第四优先:
2 [ModelBinder(typeof(FormTestModelBinderImpl4))]
3 public class FormTestModel
4 {
5 //codes
6 }

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

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

谢谢大家。

后记:

其它的相关文章:

MVC运行机制之源码剖析http://blog.csdn.net/study_live/article/details/4871745

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏