Asp.Net MVC5 檢視頁面編譯呼叫流轉過程,以及頁面Web展示
例如:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
以上是一個最簡單的View檢視返回呼叫
這個return View()執行的結果是直接返回HTML到Web頁面。這個View()是個ViewResult類,如何會返回HTML呢?在CSHTML裡面的@符號後面的C#類,變數或者是強型別Model是如何解析的?下面簡單的分析下。
View()返回的是ViewResult類的例項,繼承自abstract ViewResultBase, ViewResultBasey又繼承自抽象類ActionResult,在ActionResult裡面有一個 ExecuteResult
ExecuteResult方法會呼叫型別為ViewEngineResult的FindView方法,傳遞ControllerContext引數。獲取到當前頁面IView,IView則呼叫IView類的抽象Render方法。展開對整個CSHMTL頁面的引擎。噹噹前檢視呼叫Render方法,會導致BuildManagerCompiledView和RazorView兩個類的Render方法被呼叫。最後在RazorView的RenderView方法裡面呼叫 :webViewPage.ExecutePageHierarchy(new WebPageContext(context: viewContext.HttpContext, page: null, model: null), writer, startPage);
入棧會把當前httpResonse.Response.Output放在當前Current屬性裡,把當前環境下的textwriter入棧,作為臨時變數。頁面執行會跳轉到類StartPage裡面,StartPage.Execute會呼叫Execute運行當前的首頁或者是母版頁或者是佈局頁面。比如ViewStart.CSHTML.則會被編譯成_Page_View_ViewStart.CSHTML.繼承自ViewStartPage,當Execute被呼叫的時候,實際上是呼叫的_Page_View_ViewStart.CSHTML裡面的Execute方法。這個方法是事先被編譯好的。在Asp.Net MVC執行期間。可以通過修改MVC根目錄下webconfig配置檔案的Compilation欄位設定執行期間被編譯的CSHMTL檔案的路徑。在Compilation位元組新增tempDirectory欄位,Value為路徑名即可。在Visual Studio2015下面可以通過Reflector 9.0反編譯上面路徑下被編譯程式集。Execute被呼叫之後,會呼叫一個RunPage方法。RunPage會獲取當前頁面然後呼叫ExectuePageHierarchy方法。ExectuePageHierarchy會Base.ExectuePageHierarchy.當前頁面一般會被編譯成WebViewPage的派生類。WebViewPage又派生自WebPageBase。所以Base.ExectuePageHierarchy.會呼叫webPageBase裡面的ExectuePageHierarchy.。
Execute呼叫的是已經編譯好的CSHTML頁面。例如控制器為Account,檢視Login,那麼MVC會把它編譯成:名稱為_Page_Views_Account_Login_Cshtml的類,這個類繼承自WebViewPage<TModel>。WebViewPage<TModel>又繼承自WebViewPage,WebViewPage則繼承自WebPageBase。
WebPageBase.ExectuePageHierarchy兩個引數方法呼叫了過載的三個引數方法,添加了一個StartPage:null.重複上面的動作,入棧,因為StartPage==null,所以會呼叫WebViewPage.ExectuePageHierarchy. WebViewPage.ExectuePageHierarchy.同樣的呼叫Base.ExectuePageHierarchy.然後再呼叫Execute把當前頁面例如:_Page_Account_Login_Cshtml這種頁面的HTML以及指令碼,以及C#類語言Model等寫入當前流。至此,整個過程完成。Render呼叫結束。
_Page_Account_Login_Cshtml類的程式碼如下:
public class _Page_Views_Account_Login_cshtml : WebViewPage<LoginViewModel>
{
// Methods
public _Page_Views_Account_Login_cshtml();
public override void Execute();
// Properties
protected global_asax ApplicationInstance { get; }
// Nested Types
[CompilerGenerated]
private static class <>o__3
{
// Fields
public static CallSite<Func<CallSite, object, string, object>> <>p__0;
public static CallSite<Func<CallSite, object, object>> <>p__1;
public static CallSite<Action<CallSite, _Page_Views_Account_Login_cshtml, object>> <>p__2;
public static CallSite<Func<CallSite, object, object>> <>p__3;
public static CallSite<Func<CallSite, object, object>> <>p__4;
public static CallSite<Func<CallSite, object, string>> <>p__5;
}
}
Expand Methods
在繼承自WebStartPage和WebViewPage的編譯類中,Execute方法部分程式碼如下:
public override void Execute()
{
if (<>o__3.<>p__0 == null)
{
CSharpArgumentInfo[] argumentInfo = new CSharpArgumentInfo[] { CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) };
<>o__3.<>p__0 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.SetMember (CSharpBinderFlags.None, "Title", typeof(_Page_Views_Account_Login_cshtml), argumentInfo));
}
<>o__3.<>p__0.Target(<>o__3.<>p__0, base.get_ViewBag(), "登入");
base.BeginContext("~/Views/Account/Login.cshtml", 0x56, 8, true);
this.WriteLiteral("\r\n\r\n<h2>");
base.EndContext("~/Views/Account/Login.cshtml", 0x56, 8, true);
base.BeginContext("~/Views/Account/Login.cshtml", 0x5f, 13, false);
if (<>o__3.<>p__2 == null)
{
CSharpArgumentInfo[] infoArray2 = new CSharpArgumentInfo[] { CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, null) };
<>o__3.<>p__2 = CallSite<Action<CallSite, _Page_Views_Account_Login_cshtml, object>>.Create (Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded | CSharpBinderFlags.InvokeSimpleName, "Write", null, typeof(_Page_Views_Account_Login_cshtml), infoArray2));
}
if (<>o__3.<>p__1 == null)
{
CSharpArgumentInfo[] infoArray3 = new CSharpArgumentInfo[] { CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, null) };
<>o__3.<>p__1 = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember (CSharpBinderFlags.None, "Title", typeof(_Page_Views_Account_Login_cshtml), infoArray3));
}
<>o__3.<>p__2.Target(<>o__3.<>p__2, this, <>o__3.<>p__1.Target(<>o__3.<>p__1, base.get_ViewBag()));
base.EndContext("~/Views/Account/Login.cshtml", 0x5f, 13, false);
base.BeginContext("~/Views/Account/Login.cshtml", 0x6c, 12, true);
this.WriteLiteral("。</h2>\r\n<div");
}
類繼承結構:
WebViewPage-》WebPageBase-》WebPageRenderingBase-》WebPageExecutingBase
WebStartPage->StartPage->WebPageRenderingBase->WebPageExecutingBase
檢視類繼承方法IView-》BuildManagerCompiledView -》RazorView.