您的位置:首页 > 编程语言 > ASP

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

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: