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


前編ではBindSimpleModelメソッドについて説明しましたが、今日はBindComplexModelメソッドを見てみましょう.コードセグメント1はBindComplexModelメソッドの定義であり、実行プロセスは、まず要求されたパラメータタイプをチェックし、配列、辞書、集合、列挙、一般クラスの順に処理する.
     
   internal objectBindComplexModel(ControllerContext controllerContext, ModelBindingContextbindingContext)
        {
            object model =bindingContext.Model;
            Type modelType =bindingContext.ModelType;
 
            // if we're being asked to createan array, create a list instead, then coerce to an array after the list iscreated
            if (model == null &&modelType.IsArray)
            {
                Type elementType =modelType.GetElementType();
                Type listType =typeof(List<>).MakeGenericType(elementType);
                object collection =CreateModel(controllerContext, bindingContext, listType);
 
                ModelBindingContextarrayBindingContext = new ModelBindingContext()
                {
                    ModelMetadata =ModelMetadataProviders.Current.GetMetadataForType(() => collection,listType),
                    ModelName =bindingContext.ModelName,
                    ModelState =bindingContext.ModelState,
                    PropertyFilter =bindingContext.PropertyFilter,
                    ValueProvider =bindingContext.ValueProvider
                };
                IList list =(IList)UpdateCollection(controllerContext, arrayBindingContext, elementType);
 
                if (list == null)
                {
                    return null;
                }
 
                Array array = Array.CreateInstance(elementType,list.Count);
                list.CopyTo(array, 0);
                return array;
            }
 
            if (model == null)
            {
                model =CreateModel(controllerContext, bindingContext, modelType);
           }
 
            // special-caseIDictionary<,> and ICollection<>
            Type dictionaryType =TypeHelpers.ExtractGenericInterface(modelType, typeof(IDictionary<,>));
            if (dictionaryType != null)
            {
                Type[] genericArguments =dictionaryType.GetGenericArguments();
                Type keyType =genericArguments[0];
                Type valueType =genericArguments[1];
 
                ModelBindingContextdictionaryBindingContext = new ModelBindingContext()
               {
                    ModelMetadata =ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
                    ModelName =bindingContext.ModelName,
                    ModelState =bindingContext.ModelState,
                    PropertyFilter =bindingContext.PropertyFilter,
                    ValueProvider =bindingContext.ValueProvider
                };
                object dictionary =UpdateDictionary(controllerContext, dictionaryBindingContext, keyType, valueType);
                return dictionary;
            }
 
            Type enumerableType =TypeHelpers.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
            if (enumerableType != null)
            {
                Type elementType = enumerableType.GetGenericArguments()[0];
 
                Type collectionType =typeof(ICollection<>).MakeGenericType(elementType);
                if(collectionType.IsInstanceOfType(model))
                {
                    ModelBindingContextcollectionBindingContext = new ModelBindingContext()
                    {
                        ModelMetadata =ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
                        ModelName =bindingContext.ModelName,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter =bindingContext.PropertyFilter,
                        ValueProvider =bindingContext.ValueProvider
                    };
                    object collection = UpdateCollection(controllerContext,collectionBindingContext, elementType);
                    return collection;
                }
            }
 
            // otherwise, just update theproperties on the complex type
            BindComplexElementalModel(controllerContext,bindingContext, model);
            return model;
        }

コードセグメント1
ここでは、コードセグメント1の最後の部分のBindComplexElementalModelメソッド呼び出しを参照する必要があります.その定義は、コードセグメント2を参照してください.このコードのOnModelUpdatingとOnModelUpdatedの2つのメソッド呼び出しを見てみましょう.定義すると、この2つのメンバーはDefaultModelBinderクラスの虚メソッドであり、モデルバインド前後の検証ロジックを実行するために使用され、サブクラスはこの2つの虚メンバーを上書きして自分のロジックを実現することができ、ユーザーがモデルバインドの前または後に自分の必要な操作を行う機会を得ることができます.
     
   internal void BindComplexElementalModel(ControllerContextcontrollerContext, ModelBindingContext bindingContext, object model)
        {
            // need to replace the propertyfilter + model object and create an inner binding context
            ModelBindingContextnewBindingContext =CreateComplexElementalModelBindingContext(controllerContext, bindingContext,model);
 
            // validation
            if(OnModelUpdating(controllerContext, newBindingContext))
            {
               BindProperties(controllerContext, newBindingContext);
               OnModelUpdated(controllerContext, newBindingContext);
            }
        }

コードセグメント2
さらに、C r e a t e C o m p l e x E l e m e n t alModelBindingContextメソッドの定義(コードセグメント3)に移動します.このメソッドの最初の文を見てください.BindAttributeタイプのオブジェクトbindAttrを定義し、BindAttributeクラスに入る定義を追跡します.MVCがカスタマイズしたAttributeで、ExcludeとIncludeの2つのよく知っている属性が含まれています.
   
     internal ModelBindingContextCreateComplexElementalModelBindingContext(ControllerContext controllerContext,ModelBindingContext bindingContext, object model)
        {
            BindAttribute bindAttr =(BindAttribute)GetTypeDescriptor(controllerContext,bindingContext).GetAttributes()[typeof(BindAttribute)];
            Predicate<string>newPropertyFilter = (bindAttr != null)
                                                     ? propertyName => bindAttr.IsPropertyAllowed(propertyName) &&bindingContext.PropertyFilter(propertyName)
                                                     : bindingContext.PropertyFilter;
 
            ModelBindingContextnewBindingContext = new ModelBindingContext()
            {
                ModelMetadata =ModelMetadataProviders.Current.GetMetadataForType(() => model,bindingContext.ModelType),
                ModelName =bindingContext.ModelName,
                ModelState =bindingContext.ModelState,
                PropertyFilter =newPropertyFilter,
                ValueProvider =bindingContext.ValueProvider
            };
 
            return newBindingContext;
        }

コードセグメント3
間違いない!ここでのExcludeとIncludeプロパティは、モデルバインドを使用するときのExcludeとIncluudeです.たとえば、コードセグメント4は、JimとLily以外のユーザーがログイン操作を行うことを意味します.Bindプロパティを使用すると、特定のプロパティのバインドを許可または禁止するなど、プロジェクト内でモデルバインドに特別な制限を加えることができます.
publicActionResult Login([Bind(Exclude=”Jim,Lily”)]username)
{
}

コードセグメント4
しかしBind特性はどのようにしてモデルバインドの制限を実現したのだろうか.コードセグメント3でbindAttrを見つけました.IsPropertyAllowedメソッドは,その定義に入り,メソッドに2つのバージョンのリロードを与え,その最終実装バージョン(コードセグメント5の最初のメソッド)を見る.この方法はとても简単で、更に少し英语を理解して、注釈を见て、それを理解して完全に难しさがなくて、ほほほ!この方法は、1つの許可リストと1つの除外リストに基づいて、1つの属性がバインドを実行することを許可されているかどうかを判断する.
  
      internal static boolIsPropertyAllowed(string propertyName, ICollection<string>includeProperties, ICollection<string> excludeProperties)
        {
            // We allow a property to be boundif its both in the include list AND not in the exclude list.
            // An empty include list impliesall properties are allowed.
            // An empty exclude list implies noproperties are disallowed.
            bool includeProperty =(includeProperties == null) || (includeProperties.Count == 0) ||includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
            bool excludeProperty = (excludeProperties!= null) && excludeProperties.Contains(propertyName,StringComparer.OrdinalIgnoreCase);
            return includeProperty &&!excludeProperty;
        }
 
        public bool IsPropertyAllowed(stringpropertyName)
        {
            return IsPropertyAllowed(propertyName,_includeSplit, _excludeSplit);
        }

コードセグメント5
では、ここまでASPをNET MVCのソースコードはとても概括的に1回しごきして、言叶が无伦次で、とても筋道がなくて、どこまで书くのかと思っています.次の編から、私たちは筋道を整理します.