深入ASP.NET MVC之六:Model Binding的实现

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返回的值。附上函数调用图:

modelbind

posted @ 2012-11-27 05:50  yinzixin  阅读(3133)  评论(4编辑  收藏  举报