asp.net mvc源码分析Action篇 DefaultModelBinder

接着上篇 asp.net mvc源码分析-Controller篇 ValueProvider 现在我们来看看ModelBindingContext这个对象。

 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
            };

一般情况下FallbackToEmptyPrefix 应该是true,默认parameterDescriptor.BindingInfo.Prefix为空。里面的ModelMetadata属性是ModelMetadata的一个实例。

看看它的构造函数的定义,

  public ModelMetadata(ModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 这个类涉及到的东西很多,有些我现在也不是很明白,只知道他们是干什么的,所以这个类在这里只是简单的提一下而已。

它有一个属性

    public virtual bool IsComplexType {
            get {
                return !(TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)));
            }
        }
看看当前参数对象能否转化为string对象,如果可以则是简单对象,否则则是复杂对象。

这里的ModelMetadataProviders.Current是一个DataAnnotationsModelMetadataProvider实例。DataAnnotationsModelMetadataProvider继承于AssociatedMetadataProvider继承于AssociatedMetadataProvider继承于ModelMetadataProvider这里的调用GetMetadataForType来获取ModelMetadata,而真正创建ModelMetadata的是在DataAnnotationsModelMetadataProvider的CreateMetadata,该方法定义如下:

 protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 

该方法真正创建了一个DataAnnotationsModelMetadata实例,该类是ModelMetadata的子类。

下面 我们来看看ModelState属性=controllerContext.Controller.ViewData.ModelState

其中 ModelState非常简单

[Serializable]
    public class ModelState {
        private ModelErrorCollection _errors = new ModelErrorCollection();
        public ValueProviderResult Value { get;  set;}
        public ModelErrorCollection Errors {get {  return _errors; }
        }

而ViewDataDictionary的ModelState是一个ModelState的字典集合类ModelStateDictionary。该属性默认就只是一个实例里面没有ModelState。

下面这句binder.BindModel(controllerContext, bindingContext)是真正绑定参数的地方,我们知道默认的binder是DefaultModelBinder,所以现在我们来看看你它的BindModel方法:

   public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
            if (bindingContext == null) {
                throw new ArgumentNullException("bindingContext");
            }

            bool performedFallback = false;

            if (!String.IsNullOrEmpty(bindingContext.ModelName) && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) {
                // We couldn't find any entry that began with the prefix. If this is the top-level element, fall back
                // to the empty prefix.
                if (bindingContext.FallbackToEmptyPrefix) {
                    bindingContext = new ModelBindingContext() {
                        ModelMetadata = bindingContext.ModelMetadata,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter = bindingContext.PropertyFilter,
                        ValueProvider = bindingContext.ValueProvider
                    };
                    performedFallback = true;
                }
                else {
                    return null;
                }
            }

            // Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string))
            // or by seeing if a value in the request exactly matches the name of the model we're binding.
            // Complex type = everything else.
            if (!performedFallback) {
                bool performRequestValidation = ShouldPerformRequestValidation(controllerContext, bindingContext);
                ValueProviderResult vpResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName, skipValidation: !performRequestValidation);
                if (vpResult != null) {
                    return BindSimpleModel(controllerContext, bindingContext, vpResult);
                }
            }
            if (!bindingContext.ModelMetadata.IsComplexType) {
                return null;
            }

            return BindComplexModel(controllerContext, bindingContext);
        }

  这里面有一个判断 if (!String.IsNullOrEmpty(bindingContext.ModelName) && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))  当然正常情况下
bindingContext.ModelName是不为空的,bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)则是检查所有的ValueProvider中的所有keys是否有一个包含Action中的参数名,一般我们用的最多的是ChildActionValueProviderFactory、FormValueProviderFactory、QueryStringValueProviderFactory,用ChildActionValueProviderFactory一般是因为我们经常会有这样的代码Html.RenderAction,那么正常情况下ChildActionValueProviderFactory中就应该含有这里的bindingContext.ModelName;当我们实际参数值在FormValueProviderFactory、QueryStringValueProviderFactory中,如果我们的Action参数是简单数据类型,那么ValueProviderFactory也含有该bindingContext.ModelName,例如我们的Action定义为  public ActionResult Index(string name,string age) 访问url为http://localhost:7503/home/index?name=majiang&age=27,跑的流程主要是和Html.RenderAction调用一样,bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)为true

好让我们仔细看看

 bool performRequestValidation = ShouldPerformRequestValidation(controllerContext, bindingContext);
                ValueProviderResult vpResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName, skipValidation: !performRequestValidation);
                if (vpResult != null) {
                    return BindSimpleModel(controllerContext, bindingContext, vpResult);
                }

这几句 默认情况下performRequestValidation 为true,表示验证结果数据,而ModelBindingContext的UnvalidatedValueProvider

   internal IUnvalidatedValueProvider UnvalidatedValueProvider {
            get {
                return (ValueProvider as IUnvalidatedValueProvider) ?? new UnvalidatedValueProviderWrapper(ValueProvider);
            }
        }

这里的bindingContext.UnvalidatedValueProvider.GetValue方法我想就很好明白了,不多说了。正常情况下vpResult 也不为null

 internal object BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult valueProviderResult) {
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

            // if the value provider returns an instance of the requested data type, we can just short-circuit
            // the evaluation and return that instance
            if (bindingContext.ModelType.IsInstanceOfType(valueProviderResult.RawValue)) {
                return valueProviderResult.RawValue;
            }

            // since a string is an IEnumerable<char>, we want it to skip the two checks immediately following
            if (bindingContext.ModelType != typeof(string)) {

                // conversion results in 3 cases, as below
                if (bindingContext.ModelType.IsArray) {
                    // case 1: user asked for an array
                    // ValueProviderResult.ConvertTo() understands array types, so pass in the array type directly
                    object modelArray = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);
                    return modelArray;
                }

                Type enumerableType = TypeHelpers.ExtractGenericInterface(bindingContext.ModelType, typeof(IEnumerable<>));
                if (enumerableType != null) {
                    // case 2: user asked for a collection rather than an array
                    // need to call ConvertTo() on the array type, then copy the array to the collection
                    object modelCollection = CreateModel(controllerContext, bindingContext, bindingContext.ModelType);
                    Type elementType = enumerableType.GetGenericArguments()[0];
                    Type arrayType = elementType.MakeArrayType();
                    object modelArray = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, arrayType);

                    Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);
                    if (collectionType.IsInstanceOfType(modelCollection)) {
                        CollectionHelpers.ReplaceCollection(elementType, modelCollection, modelArray);
                    }
                    return modelCollection;
                }
            }

            // case 3: user asked for an individual element
            object model = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);
            return model;
        }

  BindSimpleModel方法相对简单,但是也还是比较复杂,我这里先看第一句

  bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

   public void SetModelValue(string key, ValueProviderResult value) {
            GetModelStateForKey(key).Value = value;

        }

  private ModelState GetModelStateForKey(string key) {
            if (key == null) {
                throw new ArgumentNullException("key");
            }
            ModelState modelState;
            if (!TryGetValue(key, out modelState)) {
                modelState = new ModelState();
                this[key] = modelState;
            }
            return modelState;
        }
从这里我们可以看到一个key对应一个ModelState 对应一个ValueProviderResult 

这个 方法最后一句
  object model = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);方法ConvertProviderResult实际就一句
               object convertedValue = valueProviderResult.ConvertTo(destinationType);意思就是把数据转换成我们需要的数据类型。

如果这里的convertedValue 为null,且bindingContext.ModelType为负责类型那么我们就要调用一次BindComplexModel,有前面的分析我们也知道bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)返回false也会调用BindComplexModel方法。

BindComplexModel方法非常复杂,这里有一句object model = bindingContext.Model; 而bindingContext.Model实际上是 return ModelMetadata.Model;具体的实现如下:

 public object Model {
            get {
                if (_modelAccessor != null) {
                    _model = _modelAccessor();
                    _modelAccessor = null;
                }
                return _model;

            }
            set {
                _model = value;
                _modelAccessor = null;
                _properties = null;
                _realModelType = null;
            }
        }

默认 情况下这个_modelAccessor==null的,在ControllerActionInvoker.GetParameterValue方法中bindingContext的 

ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),参数null造成的,GetMetadataForType的具体实现:

  private IEnumerable<ModelMetadata> GetMetadataForPropertiesImpl(object container, Type containerType) {
            foreach (PropertyDescriptor property in GetTypeDescriptor(containerType).GetProperties()) {
                Func<object> modelAccessor = container == null ? null : GetPropertyValueAccessor(container, property);
                yield return GetMetadataForProperty(modelAccessor, containerType, property);
            }
        }

现在我们又回到DefaultModelBinder的BindComplexModel中来,这里面有一句

  if (model == null) {
                model = CreateModel(controllerContext, bindingContext, modelType);
            }

所以一般情况下 BindComplexModel不会返回null值,大家要切记啊

BindComplexModel会把当前数据类型依次转化为typeof(IDictionary<,>)类型如果成功就按照字典来处理,调用UpdateDictionary方法,如果转化为typeof(IDictionary<,>失败就转化为 typeof(IEnumerable<>)按照集合来处理,调用UpdateCollection方法,如果这种转化也不行的话就按照普通的强类型来处理调用BindComplexElementalModel方法,这种绑定是我们在强类型情况下用的最多的情况。BindComplexElementalModel里面的核心代码是调用

     BindProperties(controllerContext, newBindingContext);方法,

   private void BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) {
            IEnumerable<PropertyDescriptor> properties = GetFilteredModelProperties(controllerContext, bindingContext);
            foreach (PropertyDescriptor property in properties) {
                BindProperty(controllerContext, bindingContext, property);
            }
        }

对 DefaultModelBinder的具体实现很复杂,我们在写Action时应该知道BindModel的时候里面究竟走的是BindSimpleModel还是BindComplexModel,还有参数具体是由哪个ValueProviderDictionary提供的

原文地址:https://www.cnblogs.com/majiang/p/2764456.html