在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) { |
03 |
Type parameterType = parameterDescriptor.ParameterType; |
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); |
11 |
ModelBindingContext bindingContext = new ModelBindingContext() { |
12 |
FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null ), |
13 |
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType( null , parameterType), |
14 |
ModelName = parameterName, |
15 |
ModelState = controllerContext.Controller.ViewData.ModelState, |
16 |
PropertyFilter = propertyFilter, |
17 |
ValueProvider = valueProvider |
20 |
object result = binder.BindModel(controllerContext, bindingContext); |
21 |
return result ?? parameterDescriptor.DefaultValue; |
24 |
private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) { |
26 |
return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType); |
这里优先从 parameterDescriptor.BindingInfo中得到IModelBinder, 接下来我们看一下怎么从parameterDescriptor中获取binder
首先找到 ReflectedActionDescriptor对象,可以看到在GetParameters方法中生成了ReflectedParameterDescriptor 对象,
System.Web.Mvc.ReflectedActionDescriptor
01 |
public override ParameterDescriptor[] GetParameters() { |
02 |
ParameterDescriptor[] parameters = LazilyFetchParametersCollection(); |
05 |
return (ParameterDescriptor[])parameters.Clone(); |
08 |
private ParameterDescriptor[] LazilyFetchParametersCollection() { |
09 |
return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>( |
10 |
ref _parametersCache , |
11 |
MethodInfo.GetParameters , |
12 |
parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this ) ); |
这个对象中调用了ModelBinders.GetBinderFromAttributes方法来获取binder
ReflectedParameterDescriptor -> ReflectedParameterBindingInfo
1 |
public override IModelBinder Binder { |
3 |
IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo, |
4 |
() => String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes, |
5 |
_parameterInfo.Name, _parameterInfo.Member)); |
System.Web.Mvc.ModelBinders
1 |
internal static IModelBinder GetBinderFromAttributes(ICustomAttributeProvider element, Func< string > |
2 |
errorMessageAccessor) { |
3 |
CustomModelBinderAttribute[] attrs = (CustomModelBinderAttribute[])element. |
4 |
GetCustomAttributes( typeof (CustomModelBinderAttribute), |
7 |
return GetBinderFromAttributesImpl(attrs, errorMessageAccessor); |
接下来我们看
return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
语句后面的的分支.
System.Web.Mvc.ModelBinderDictionary
01 |
private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) { |
09 |
IModelBinder binder = _modelBinderProviders.GetBinder(modelType); |
14 |
if (_innerDictionary.TryGetValue(modelType, out binder)) { |
18 |
binder = ModelBinders.GetBinderFromAttributes(modelType, |
19 |
() => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName)); |
21 |
return binder ?? fallbackBinder; |
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); |
到这里我们就清楚的知道了获取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" ); |
6 |
return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null ); |
示例代码:
下面我们通过实例代码来印证以上的结论。
02 |
public class FormTestModel |
05 |
[DataType(DataType.Text)] |
06 |
[Display(Name = "Room Name" )] |
07 |
public string RoomName { get ; set ; } |
10 |
[DataType(DataType.Text)] |
11 |
[Display(Name = "Room Count" )] |
12 |
public string RoomCount { get ; set ; } |
18 |
public ActionResult FormTest(FormTestModels model, string returnUrl) |
20 |
<span style= "white-space:pre" > </span> return view(); |
24 |
internal sealed class FormTestModelBinderImpl1 : IModelBinder |
26 |
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) |
28 |
DefaultModelBinder defBinder = new DefaultModelBinder(); |
29 |
return defBinder.BindModel(controllerContext, bindingContext); |
32 |
internal sealed class FormTestModelBinderImpl2 : IModelBinder |
34 |
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) |
36 |
DefaultModelBinder defBinder = new DefaultModelBinder(); |
37 |
return defBinder.BindModel(controllerContext, bindingContext); |
40 |
internal sealed class FormTestModelBinderImpl3 : IModelBinder |
42 |
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) |
44 |
DefaultModelBinder defBinder = new DefaultModelBinder(); |
45 |
return defBinder.BindModel(controllerContext, bindingContext); |
48 |
internal sealed class FormTestModelBinderImpl4 : IModelBinder |
50 |
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) |
52 |
DefaultModelBinder defBinder = new DefaultModelBinder(); |
53 |
return defBinder.BindModel(controllerContext, bindingContext); |
57 |
public sealed class FormTestModelBinderProviderImpl : IModelBinderProvider |
59 |
public IModelBinder GetBinder(Type modelType) |
61 |
if (modelType == typeof (FormTestModels)) |
64 |
return new FormTestModelsModelBinderImpl2(); |
04 |
public ActionResult FormTest([ModelBinder( typeof (FormTestModelBinderImpl1))] FormTestModel model, string returnUrl) |
10 |
ModelBinderProviders.BinderProviders.Add( new FormTestModelBinderProviderImpl2()); |
12 |
System.Web.Mvc.ModelBinders.Binders[ typeof (FormTestModel)] = new FormTestModelBinderImpl3(); |
2 |
[ModelBinder( typeof (FormTestModelBinderImpl4))] |
3 |
public class FormTestModel |
最后我们在四个binder上面设置断点,看一下代码执行的情况,就可以清楚的看到我们的结论是多么的无比正确.:)
最后感谢大家观看,以上分析如有雷同纯属巧合。
谢谢大家。
后记:
其它的相关文章:
MVC运行机制之源码剖析http://blog.csdn.net/study_live/article/details/4871745