带你读开源—ASP.NET_MVC(十三)

        今天紧接上篇的进度,继续讨论ModelBinding。

        为了衔接顺畅,把上篇最后一段代码再贴一遍,姑且称作代码段1吧。

 

       protected virtual objectGetParameterValue(ControllerContext controllerContext, ParameterDescriptorparameterDescriptor)
        {
            // collect all of the necessarybinding properties
            Type parameterType =parameterDescriptor.ParameterType;
            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 prefixnot 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;
        }

代码段 1

        这里关键要搞清两点:一是MVC如何得到IModelBinder接口的实例,二是该接口的BindModel方法如何把Request相关信息绑定到相应Model之上。

先看第一点,在代码段1中找到GetModelBinder方法的调用,按F12进入其定义(代码段2)。这个方法只有一条语句,结合“// look on the parameter itself, then look in the global table”这条注释,可以知道MVC先检查传进来的parameterDescriptor.BindingInfo.Binder是否为空,不为空则直接返回该属性,若为空则通过调用Binders.GetBinder方法,在全局表里寻找IModelBinder。

        private IModelBinderGetModelBinder(ParameterDescriptor parameterDescriptor)
        {
            // look on the parameter itself,then look in the global table
            returnparameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
        }


代码段 2

        进入Binders.GetBinder方法的定义,它有很多重载,直接找到最终调用的版本(代码段3)。

        private IModelBinder GetBinder(TypemodelType, IModelBinder fallbackBinder)
        {
            // Try to look up a binder for thistype. We use this order of precedence:
            // 1. Binder returned from provider
            // 2. Binder registered in theglobal table
            // 3. Binder attribute defined onthe type
            // 4. Supplied fallback binder
 
            IModelBinder binder =_modelBinderProviders.GetBinder(modelType);
            if (binder != null)
            {
                return binder;
            }
 
            if(_innerDictionary.TryGetValue(modelType, out binder))
            {
                return binder;
            }
 
            // Function is called frequently,so ensure the error delegate is stateless
            binder =ModelBinders.GetBinderFromAttributes(modelType, (Type errorModel) =>
                {
                    throw new InvalidOperationException(
                       String.Format(CultureInfo.CurrentCulture,MvcResources.ModelBinderDictionary_MultipleAttributes, errorModel.FullName));
                });
 
            return binder ?? fallbackBinder;
        }


代码段 3

        微软的开源项目组还是比较重视代码注释的,瞧,这个方法一上来就是一段相当工整的注释。尽管各位的英文都是相当的NB,我还是献丑翻译一下吧:

尝试寻找该类型的binder。我们按照如下顺序进行搜索:

        1、provider返回的Binder

        2、在全局表里注册的Binder

        3、在该类型上定义的Binder特性

        4、用户提供的备用Binder

        下面逐条讨论这四条的实现原理:

        1、provider返回的Binder。

        找到如下语句:

        IModelBinderbinder = _modelBinderProviders.GetBinder(modelType);

        我们看看modelBinderProviders这个私有成员是怎么赋值的,找到其所在类ModelBinderDictionary的构造函数(代码段4)。

        public ModelBinderDictionary()
            :this(ModelBinderProviders.BinderProviders)
        {
        }
 
        internal ModelBinderDictionary(ModelBinderProviderCollectionmodelBinderProviders)
        {
            _modelBinderProviders =modelBinderProviders;
        }


代码段 4

        可见modelBinderProviders是用ModelBinderProviders.BinderProviders赋值的,而ModelBinderProviders.BinderProviders【1】又是什么东东呢?我们看代码段5,这是我们在自己的MVC项目中Global.asax文件中的Application_Start方法,其中第二句(//*处)是我们把自定义的mModelBinderProvider添加到ModelBinderProviders.BinderProviders集合中,这个集合就是上标【1】处的那个什么东东。

protected voidApplication_Start()
{
AreaRegistration.RegisterAllAreas();
 
ModelBinderProviders.BinderProviders.Add(newCustomModelBinderProvider());//*
 
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}


代码段 5

        2、在全局表里注册的Binder。

        这个比较简单,就一条代码【_innerDictionary.TryGetValue(modelType, out binder)】,其中的innerDictionary是一个字典对象,意思是在这个字典中寻找该类型的Binder。

        3、在该类型上定义的Binder特性。

        在代码段3中找到ModelBinders.GetBinderFromAttributes方法,从名称可以猜测出它的作用是从Attributes中获取Binder。下面转到其定义(代码段6),这段代码首先通过GetAttributes()获取类型type的所有特性(Attributes),再调用SingleOfTypeDefaultOrError方法得到用户自定义的CustomModelBinderAttribute,最后通过调用GetBinder方法获得IModelBinder并返回之。

       internal static IModelBinder GetBinderFromAttributes(Type type,Action<Type> errorAction)
       {
           AttributeList allAttrs = newAttributeList(TypeDescriptorHelper.Get(type).GetAttributes());
           CustomModelBinderAttributebinder = allAttrs.SingleOfTypeDefaultOrError<Attribute,CustomModelBinderAttribute, Type>(errorAction, type);
           return binder == null ? null : binder.GetBinder();
       }


代码段 6

        SingleOfTypeDefaultOrError方法的定义见代码段7,这是IList<TInput>泛型类的一个扩展方法,作用是从一个IList集合中搜索类型是TMatch的元素,如果集合中只有一个匹配,则返回该元素;若没有匹配,则返回空;若有多于一个的匹配,则调用回调委托errorAction。

       /// <summary>
       /// Returns a single value in list matching type TMatch if there is onlyone, null if there are none of type TMatch or calls the
       /// errorAction with errorArg1 if there is more than one.
       /// </summary>
       public static TMatch SingleOfTypeDefaultOrError<TInput, TMatch,TArg1>(this IList<TInput> list, Action<TArg1> errorAction, TArg1errorArg1) where TMatch : class
       {
           Contract.Assert(list != null);
           Contract.Assert(errorAction != null);
 
           TMatch result = null;
           for (int i = 0; i < list.Count; i++)
           {
                TMatch typedValue = list[i] asTMatch;
                if (typedValue != null)
                {
                    if (result == null)
                    {
                        result = typedValue;
                    }
                    else
                    {
                        errorAction(errorArg1);
                        return null;
                    }
                }
           }
           return result;
       }


代码段 7

 

你可能感兴趣的:(mvc,.net,开源,asp.net,开源代码)