在Filter和Action的執行 中說到,ControllerActionInvoker對象在InvokeAction方法中調用了GetParameters方法實現了model binding,先來看下這個方法:
protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters(); foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors) { parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor); } return parametersDict; }
首先通過actionDescriptor獲得action參數的信息,這里的actionDescriptor實際上是一個ReflectedActionDescriptor,獲得參數的方法自然是通過反射,不深入分析了。緊接着就對每個參賽調用了GetParameterValue方法:
protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) { // collect all of the necessary binding 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 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; }
這個方法中完成了幾件事,首先是獲得Model Binder,其次是獲得Value Provider,再獲得Property Filter,最終把這些信息組成ModelBindingContext,交給binder的BindModel方法實現綁定。先看如何獲得Model Binder和Value Provider,這里將是擴展model binding的着手點。GetModelBinder方法如下:
private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) { // look on the parameter itself, then look in the global table return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType); }
如果通過action的參數的Atrribute說明了采用什么binder的話就優先使用這個binder,否則從全局binder中查找。這里Binders是一個ModelBinderDictionary對象,其初始化是在ModelBinders類中的CreateDefaultBinderDictionary方法:
private static ModelBinderDictionary CreateDefaultBinderDictionary() { // We can't add a binder to the HttpPostedFileBase type as an attribute, so we'll just // prepopulate the dictionary as a convenience to users. ModelBinderDictionary binders = new ModelBinderDictionary() { { typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder() }, { typeof(byte[]), new ByteArrayModelBinder() }, { typeof(Binary), new LinqBinaryModelBinder() } }; return binders; }
在這里准備了幾個默認的ModelBinder.准備工作完成之后,看真正的GetBinder方法,這個方法經過幾個重載的方法之后,最終調用的是如下的方法:
return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null);
這里fallbackToDefault是true,DefaultBinder就是一個DefaultModelBinder對象,這個方法的實現如下:
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; }
加上注釋的幫助,很容易理解獲取一個類型對應的model binder的過程是如何的。首先是通過IModelBinderProvider來查找,這里的modelBinderProviders在默認情況下就是ModelBinderProviders.BinderProviders屬性,這是ModelBinderProviderCollection類型的對象。我們要使用自定義的model binder,一個方法就是實現一個IModelBinderProivder,並且通過ModelBinderProviders.BinderProviders.Add方法注冊到全局的provider表中。第二個途徑是通過現有的binder表,這里的_innerDictionary是一個Dictionary<Type, IModelBinder>類型的對象,也就是ModelBinderDictionary實際存儲數據的地方,ModelBinders.Binders.Add方法就會直接往這個dictionary中添加數據。第三個途徑是通過待綁定類型的Attribute來加載binder。最后,如果上面的途徑都沒有找到binder,那么就用默認的DefaultModelBinder,大多數時候這個DefaultModelBinder已經足夠強大。暫時先跳過這個DefaultModelBinder的實現,再回到GetParameterValue中,當得到合適的model binder之后還需要獲得value provider:
IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
這里的ValueProvider是定義在ControllerBase類型中的:
public IValueProvider ValueProvider { get { if (_valueProvider == null) { _valueProvider = ValueProviderFactories.Factories.GetValueProvider(ControllerContext); } return _valueProvider; } set { _valueProvider = value; } }
下面來看下 ValueProviderFactories的實現:
public static class ValueProviderFactories { private static readonly ValueProviderFactoryCollection _factories = new ValueProviderFactoryCollection() { new ChildActionValueProviderFactory(), new FormValueProviderFactory(), new JsonValueProviderFactory(), new RouteDataValueProviderFactory(), new QueryStringValueProviderFactory(), new HttpFileCollectionValueProviderFactory(), }; public static ValueProviderFactoryCollection Factories { get { return _factories; } } }
這個類初始化了一系列默認的ValueProviderFactory。再看下GetValueProvider(ControllerContext) 這個方法的實現:
public IValueProvider GetValueProvider(ControllerContext controllerContext) { var valueProviders = from factory in _serviceResolver.Current let valueProvider = factory.GetValueProvider(controllerContext) where valueProvider != null select valueProvider; return new ValueProviderCollection(valueProviders.ToList()); }
這里的做法是返回所有的能夠找到的value provider,再將其組合成一個ValueProviderCollection.再看下ValueProviderCollection的關鍵方法的實現:
public virtual ValueProviderResult GetValue(string key) { return GetValue(key, skipValidation: false); } public virtual ValueProviderResult GetValue(string key, bool skipValidation) { return (from provider in this let result = GetValueFromProvider(provider, key, skipValidation) where result != null select result).FirstOrDefault(); } internal static ValueProviderResult GetValueFromProvider(IValueProvider provider, string key, bool skipValidation) { // Since IUnvalidatedValueProvider is a superset of IValueProvider, it's always OK to use the // IUnvalidatedValueProvider-supplied members if they're present. Otherwise just call the // normal IValueProvider members. IUnvalidatedValueProvider unvalidatedProvider = provider as IUnvalidatedValueProvider; return (unvalidatedProvider != null) ? unvalidatedProvider.GetValue(key, skipValidation) : provider.GetValue(key); }
這個方法返回的是第一個能夠找到value的ValueProvider返回的值。附上函數調用圖: