あなたをオープンソースに連れて行きます-ASP.NET_MVC(十二)
5430 ワード
今日は主に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メンバーにアクセスできるようになったからです.
コードセグメント1
なぜRazorでActionが伝えたモデルを直接「Model」で表すことができるのでしょうか.システムを見てみましょうWeb.Mvcの下のControllerクラスにおけるViewメソッドの定義は、コードセグメント2を参照してください.このViewメソッドは、Actionでよく使用されるActionResultを返す方法です.コードセグメント2がAction中のモデルをViewDataに渡すことがわかる.Model、コードセグメント1はViewデータをModelはModelに渡されているので、RazorビューでModelでActionから渡されたModelにアクセスできます.
コードセグメント2
ASPに従う.NETMVCの大まかな要求処理パイプラインフロー,すなわち「Request->Action->View」は,2つの概念を強調する必要がある.一つは,先ほど議論したActionからViewへのModel伝達,もう一つはRequestからActionへのパラメータバインド,いわゆる「モデルバインド(ModelBinding)」である.
例を挙げて、コードセグメント3を参照してください.
コードセグメント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の呼び出しを完了する.
コードセグメント4
ではMVCはどのようにしてアクションパラメータを取得したのでしょうか.GetParameterValueメソッドの定義(コードセグメント5)を見つけた.この方法は脈絡がはっきりしているので,まずactionDescriptorを用いる.GetParameters()メソッドは、ターゲットアクションのパラメータを取得し、foreachループで各パラメータについてGetParameterValueメソッドで対応する実パラメータを取得し、辞書に格納する.
コードセグメント5
コードセグメント5の鍵はGetParameterValueメソッドが実パラメータを取得するプロセスであり,MVCはIModelBinderインタフェースインスタンスのBindModelメソッドを呼び出してRequestとActionパラメータを関連付け,objectタイプオブジェクトを返す.
コードセグメント6
強タイプビューでは、"@model"キーに対応して強タイプを定義し、"@Model"キーでActionから渡されたモデルインスタンスを参照できます.多くの人、特に初心者はこの点について理解しにくく、いつ小文字のモデルを使うのか、いつ大文字のモデルを使うのか、ここではその経緯を探ってみましょう.
なぜ自分のcshtmlファイルでModelにアクセスできるのかを見てみましょう.これはWebViewPage
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