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

        上回书说到ModelBinderDictionary类的GetBinder方法的实现原理,今天继续解释第四条注释。

        4、用户提供的备用Binder

        这一条很简单,fallbackBinder是用户提供的一个备用Binder,从语句【returnbinder ?? fallbackBinder;】可以看出,如果第三条也没有得到合适的IModelBinder的话,则返回由用户提供的备用fallbackBinder。

        上篇提到理解模型绑定需要搞清的两个要点:一是MVC如何得到IModelBinder接口的实例,二是该接口的BindModel方法如何把Request相关信息绑定到相应Model之上。第一点在上篇中已经解释过,下面我们看第二点,即:该接口的BindModel方法如何把Request相关信息绑定到相应Model之上。

        在ControllerActionInvoker类的GetParameterValue方法中找到代码:【objectresult = binder.BindModel(controllerContext, bindingContext);】,这句是执行模型绑定的实际代码。把鼠标放在BindModel方法上,按F12进入其定义(代码段1)。

    public interface IModelBinder
    {
        object BindModel(ControllerContextcontrollerContext, ModelBindingContext bindingContext);
    }


代码段 1

        我们发现BindModel方法是接口IModelBinder的一个方法,当然,接口IModelBinder非常简单,只有一个方法。所以我们猜测肯定有某些类实现了IModelBinder接口,通过在源码中查找,找到了该接口的实现类有:ByteArrayModelBinder、CancellationTokenModelBinder、DefaultModelBinder、FormCollectionModelBinder、HttpPostedFileBaseModelBinder、DeserializingModelBinder、ExtensibleModelBinderAdapter、ResourceModelBinder以及NullBinder。是不是有蒙圈的感觉?好吧,我也蒙了,这多一大堆。

        我们只看较为常用的DefaultModelBinder中关于BindModel方法的实现,见代码段2。这个方法比较长,我们只挑重点的部分看。其实该方法总体上是把需要绑定的模型分为简单模型和复杂模型,分别由BindSimpleModel和BindComplexModel两个方法负责具体实现。根据注释可知,MVC把诸如int、string等简单数据类型作为简单模型,其它类作为复杂类型。

     

   public virtual objectBindModel(ControllerContext controllerContext, ModelBindingContextbindingContext)
        {
           RuntimeHelpers.EnsureSufficientExecutionStack();
 
            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 withthe prefix. If this is the top-level element, fall back
                // to the empty prefix.
                if(bindingContext.FallbackToEmptyPrefix)
                {
                    bindingContext = newModelBindingContext()
                    {
                        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 therequest exactly matches the name of the model we're binding.
            // Complex type = everything else.
            if (!performedFallback)
            {
                bool performRequestValidation =ShouldPerformRequestValidation(controllerContext, bindingContext);
                ValueProviderResultvalueProviderResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName,skipValidation: !performRequestValidation);
                if (valueProviderResult !=null)
                {
                    returnBindSimpleModel(controllerContext, bindingContext, valueProviderResult);
                }
            }
            if(!bindingContext.ModelMetadata.IsComplexType)
            {
                return null;
            }
 
            returnBindComplexModel(controllerContext, bindingContext);
        }

代码段 2

        我们分别来看BindSimpleModel方法和BindComplexModel方法。

        代码段3为BindSimpleModel方法的定义,该方法的条理还是比较清晰的,注释也比较完备。大概执行过程是:如果valueprovider返回的是请求的数据类型,则不再执行接下来的代码,而直接返回该valueprovider的值;否则检查请求的类型,分别按照数组、集合和普通类的次序进行处理。

    

    internal objectBindSimpleModel(ControllerContext controllerContext, ModelBindingContextbindingContext, ValueProviderResult valueProviderResult)
        {
           bindingContext.ModelState.SetModelValue(bindingContext.ModelName,valueProviderResult);
 
            // if the value provider returns aninstance of the requested data type, we can just short-circuit
            // the evaluation and return thatinstance
            if(bindingContext.ModelType.IsInstanceOfType(valueProviderResult.RawValue))
            {
                return valueProviderResult.RawValue;
            }
 
            // since a string is anIEnumerable<char>, we want it to skip the two checks immediatelyfollowing
            if (bindingContext.ModelType !=typeof(string))
            {
                // conversion results in 3 cases,as below
                if(bindingContext.ModelType.IsArray)
                {
                    // case 1: user asked foran array
                    //ValueProviderResult.ConvertTo() understands array types, so pass in the arraytype 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 acollection 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 anindividual element
            object model =ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName,valueProviderResult, bindingContext.ModelType);
            return model;
        }

代码段 3

        未完待续。。。

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