Asp.net MVC源码分析 -- 获取ModelBinder的优先级
2011-11-18 16:11
806 查看
在asp.net mvc 框架中我们可以对System.Web.Mvc.Binders 进行扩展我们自定义的binder 类型,但是同时它还有一些其它的方法可以实现自定义的model binder.而且mvc在使用的时候还有一些策略,现分析如下:
获取ModelBinder 对象的入口方法是GetParameterValue, 其中
IModelBinder binder = GetModelBinder(parameterDescriptor);
这一句代码决定了ModelBinder 的使用策略。
System.Web.Mvc.ControllerActionInvoker
这里优先从 parameterDescriptor.BindingInfo中得到IModelBinder, 接下来我们看一下怎么从parameterDescriptor中获取binder
首先找到 ReflectedActionDescriptor对象,可以看到在GetParameters方法中生成了ReflectedParameterDescriptor 对象,
System.Web.Mvc.ReflectedActionDescriptor
这个对象中调用了ModelBinders.GetBinderFromAttributes方法来获取binder
ReflectedParameterDescriptor -> ReflectedParameterBindingInfo
System.Web.Mvc.ModelBinders
接下来我们看
return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
语句后面的的分支.
System.Web.Mvc.ModelBinderDictionary
到这里我们就清楚的知道了获取IModelBinder的优先级。
结论:
1.如果在Action参数中的对象中标记了元数据的IModelBinder 实现时则调用该实现。//注:如果这句不理解可以继续看下面的实例
2.如果在参数中没有标记元数据的IModelBinder 实现 ,则调用的优先顺序为:
// (1).从全局的ModelBinderProviders.BinderProviders 中查找,我们也可以注册自己的provider
// (2).从全局的System.Web.Mvc.Binders 对象中查找,自己也可以注册自己的binder
// (3). 从参数对象的类型中的attribute 找到 ModelBinderAtribute中的IModelBinder 实现.
// (4). 最后如果经过以上步骤都没有找到IModelBinder的实现的话,就用MVC默认的实现DefaultModelBinder类,
示例代码:
下面我们通过实例代码来印证以上的结论。
Model 类:
Action
我们新建四个IModelBinder 的实现;
然后我们分别加入:
第一优先:
第二优先:
第三优先:
第四优先:
最后我们在四个binder上面设置断点,看一下代码执行的情况,就可以清楚的看到我们的结论是多么的无比正确.:)
最后感谢大家观看,以上分析如有雷同纯属巧合。
谢谢大家。
后记:
相关文章:
MVC运行机制之源码剖析http://blog.csdn.net/study_live/article/details/4871745
获取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
相关文章推荐
- asp.net mvc源码分析-Action篇 IModelBinder
- asp.net mvc源码分析-DefaultModelBinder 集合绑定
- asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证
- asp.net mvc源码分析-DefaultModelBinder 集合绑定
- asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证
- asp.net mvc源码分析-Action篇 DefaultModelBinder
- asp.net mvc源码分析-Action篇 DefaultModelBinder
- asp.net mvc源码分析-Action篇 IModelBinder
- asp.net mvc源码分析-Action篇 IModelBinder
- asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证
- Asp.net MVC DefaultModelBinder分析
- Asp.net MVC 示例项目"Suteki.Shop"分析之---ModelBinder
- Asp.net MVC 示例项目"Suteki.Shop"分析之---ModelBinder 推荐
- Asp.net MVC 示例项目"Suteki.Shop"分析之---ModelBinder
- asp.net mvc源码分析-ModelValidatorProviders
- asp.net MVC利用自定义ModelBinder过滤关键字的方法(附demo源码下载)
- asp.net MVC利用自定义ModelBinder过滤关键字的方法(附demo源码下载)
- 深入分析 ASP.NET Mvc 1.0 – 4. 使用ModelBinder绑定Action的参数
- Asp.net MVC DefaultModelBinder分析
- ASP.NET MVC 2扩展点之Model Binder实例分析