zoukankan      html  css  js  c++  java
  • 深入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

  • 相关阅读:
    2019-4-16-C#-使用反射获取私有属性的方法
    2019-5-21-C#-命令行如何静默调用-del-删除文件
    2019-6-14-WPF-shows-that-some-windows-in-multithreading-will-be-locked-in-the-PenThreadWorker-constr...
    2019-9-11-完整的-P2P-应用需要包含哪些功能
    2019-3-1-VisualStudio-扩展开发-获得输出窗口内容
    2018-10-19-C#-序列类为-xml-可以使用的特性大全
    2018-8-10-win10-uwp-手把手教你使用-asp-dotnet-core-做-cs-程序
    2018-10-31-win10-uwp-使用-asp-dotnet-core-做图床服务器客户端
    2018-8-10-docfx-做一个和微软一样的文档平台
    linux分区方案
  • 原文地址:https://www.cnblogs.com/yinzixin/p/2790140.html
Copyright © 2011-2022 走看看