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


今日は主にWebViewPageというクラスについて話しますが、なぜ急にこのクラスを研究したのでしょうか.ビューのRazorビューエンジンを覚えていますcshtmlファイルでしょう?MVCはすぐにC#のクラスに翻訳され、このクラスはWebViewPageクラスに継承されます.強いタイプのRazorビューでは、.cshtmlファイルはWebViewPage汎用クラスに翻訳され、ビューページの上部に「@model string[]」のような東東で強いタイプのViewを定義できるのは、MVCがWebViewPage汎用クラスのタイプパラメータと考えているからである.弱いタイプはWebViewPage非汎用クラスに翻訳されます.
強タイプビューでは、"@model"キーに対応して強タイプを定義し、"@Model"キーでActionから渡されたモデルインスタンスを参照できます.多くの人、特に初心者はこの点について理解しにくく、いつ小文字のモデルを使うのか、いつ大文字のモデルを使うのか、ここではその経緯を探ってみましょう.
なぜ自分のcshtmlファイルでModelにアクセスできるのかを見てみましょう.これはWebViewPageの定義にModelという名前の属性(コードセグメント1)があり、私たちが自分で書いたRazorビュークラスはWebViewPageのサブクラスに翻訳され、Modelメンバーにアクセスできるようになったからです.
        public new TModel Model
        {
            get { return ViewData.Model; }
        }

コードセグメント1
なぜRazorでActionが伝えたモデルを直接「Model」で表すことができるのでしょうか.システムを見てみましょうWeb.Mvcの下のControllerクラスにおけるViewメソッドの定義は、コードセグメント2を参照してください.このViewメソッドは、Actionでよく使用されるActionResultを返す方法です.コードセグメント2がAction中のモデルをViewDataに渡すことがわかる.Model、コードセグメント1はViewデータをModelはModelに渡されているので、RazorビューでModelでActionから渡されたModelにアクセスできます.
        protected internal virtual ViewResultView(string viewName, string masterName, object model)
        {
            if (model != null)
            {
                ViewData.Model = model;
            }
 
            return new ViewResult
            {
                ViewName = viewName,
                MasterName = masterName,
                ViewData = ViewData,
                TempData = TempData,
                ViewEngineCollection =ViewEngineCollection
            };
        }

コードセグメント2
ASPに従う.NETMVCの大まかな要求処理パイプラインフロー,すなわち「Request->Action->View」は,2つの概念を強調する必要がある.一つは,先ほど議論したActionからViewへのModel伝達,もう一つはRequestからActionへのパラメータバインド,いわゆる「モデルバインド(ModelBinding)」である.
例を挙げて、コードセグメント3を参照してください.
        public ActionResult ProductDetail (intproductID)
        {
            return View();
        }

コードセグメント3
アドレス「/Home/ProductDetail?productID=1」にアクセスすると、MVCはクエリ文字列のproductIDの値をProductDetailというActionに自動的に渡してパラメータとして使用します.もちろん、クエリ文字列はMVCがActionパラメータを検索する一つの選択にすぎず、RouteData、Requestからも可能である.FormなどのHTTPリクエスト情報で検索します.では、MVCはどのようにしてRequestとActionパラメータを結びつけるのでしょうか.これがModelBindingの役割です.次に、真相を探ってみましょう.
MVCソースでSystemを見つけます.Web.ControllerActionInvokerクラスのInvokeActionメソッド.このメソッドはMVCがActionメソッドを呼び出す場所であり、コードセグメント4はそのメソッドの一部である.MVCは、まずActionNameに従って対応するActionメソッドを見つけ、GetParameterValueメソッドでActionに必要なパラメータを取得し、Actionの呼び出しを完了する.
IDictionary<string,object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContextpostActionContext = InvokeActionMethodWithFilters(controllerContext,filterInfo.ActionFilters, actionDescriptor, parameters);

コードセグメント4
ではMVCはどのようにしてアクションパラメータを取得したのでしょうか.GetParameterValueメソッドの定義(コードセグメント5)を見つけた.この方法は脈絡がはっきりしているので,まずactionDescriptorを用いる.GetParameters()メソッドは、ターゲットアクションのパラメータを取得し、foreachループで各パラメータについてGetParameterValueメソッドで対応する実パラメータを取得し、辞書に格納する.
 
       protected virtualIDictionary<string, object> GetParameterValues(ControllerContextcontrollerContext, ActionDescriptor actionDescriptor)
        {
            Dictionary<string, object>parametersDict = new Dictionary<string,object>(StringComparer.OrdinalIgnoreCase);
            ParameterDescriptor[]parameterDescriptors = actionDescriptor.GetParameters();
 
            foreach (ParameterDescriptorparameterDescriptor in parameterDescriptors)
            {
               parametersDict[parameterDescriptor.ParameterName] =GetParameterValue(controllerContext, parameterDescriptor);
            }
            return parametersDict;
        }

コードセグメント5
コードセグメント5の鍵はGetParameterValueメソッドが実パラメータを取得するプロセスであり,MVCはIModelBinderインタフェースインスタンスのBindModelメソッドを呼び出してRequestとActionパラメータを関連付け,objectタイプオブジェクトを返す.
        protected virtual objectGetParameterValue(ControllerContext controllerContext, ParameterDescriptorparameterDescriptor)
        {
            // collect all of the necessarybinding 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 prefixnot 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;
        }

コードセグメント6