あなたをオープンソースに連れて行きます-ASP.NET_MVC(14)


前回の本ではModelBinderDictionaryクラスのGetBinderメソッドの実現原理について述べたが,今日は第4の注釈を引き続き説明する.
4、ユーザー提供の代替Binder
これは簡単です.fallbackBinderはユーザーが提供した代替Binderで、文から「returnbinder??fallbackBinder;」第3条においても適切なIModelBinderが得られなければ、ユーザによって提供された代替fallbackBinderが返されることが分かる.
前編では,モデルバインドの理解に必要な2つの要点について述べた.1つはMVCがIModelBinderインタフェースのインスタンスをどのように得るか,2つは,このインタフェースのBindModelメソッドがRequest関連情報を対応するModelにどのようにバインドするかである.第1点は前編で説明したが,次に,インタフェースのBindModelメソッドがRequest関連情報を対応するModelにバインドする方法を見る.
ControllerActionInvokerクラスのGetParameterValueメソッドでコードを見つけます:【objectresult=binder.BindModel(controllerContext,bindingContext)】,この文は、モデルバインドを実行する実際のコードです.BindModelメソッドにマウスを置き、F 12を押して定義(コードセグメント1)に入ります.
    public interface IModelBinder
    {
        object BindModel(ControllerContextcontrollerContext, ModelBindingContext bindingContext);
    }
コードセグメント1
BindModel法はインタフェースIModelBinderの1つの方法であることを見出したが,もちろんインタフェースIModelBinderは非常に簡単で,1つの方法しかない.したがって、いくつかのクラスがIModelBinderインタフェースを実装していると推測され、ソースコードで検索することによって、このインタフェースを見つけた実装クラスは、B y t e A r ayModelBinder、CancellationTokenModelBinder、DefaultModelBinder、FormCollectionModelBinder、H t p PostedFileBaseModelBinder、DeserializingModelBinder、E x t e n sibleModelBinder Adapter、ResourceModelBinder、NullBinderである.輪をかぶった感じがしますか?はい、私も隠しました.これはたくさんあります.
コードセグメント2を参照して、比較的一般的なDefaultModelBinderにおけるBindModelメソッドの実装のみを参照します.この方法は比較的長いので,私たちは重点の部分だけを選んで見ます.実はこの方法は全体的にバインドが必要なモデルを単純なモデルと複雑なモデルに分け,BindSimpleModelとBindComplexModelの2つの方法が具体的な実現を担当している.注記から分かるように、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
未完待续..