1. 程式人生 > >Asp.Net MVC5 檢視頁面編譯呼叫流轉過程,以及頁面Web展示

Asp.Net MVC5 檢視頁面編譯呼叫流轉過程,以及頁面Web展示

  當控制器呼叫Action,返回View的時候。

         例如:

    public class HomeController : Controller

    {

        public ActionResult Index()

        {

            return View();

        }

    }

以上是一個最簡單的View檢視返回呼叫 

這個return View()執行的結果是直接返回HTMLWeb頁面。這個View()是個ViewResult類,如何會返回HTML呢?在CSHTML裡面的@符號後面的C#類,變數或者是強型別Model是如何解析的?下面簡單的分析下。



View()返回的是ViewResult類的例項,繼承自abstract  ViewResultBase, ViewResultBasey又繼承自抽象類ActionResult,在ActionResult裡面有一個 ExecuteResult

抽象方法,引數為控制器上下文.

 

ExecuteResult方法會呼叫型別為ViewEngineResultFindView方法,傳遞ControllerContext引數。獲取到當前頁面IViewIView則呼叫IView類的抽象Render方法。展開對整個CSHMTL頁面的引擎。噹噹前檢視呼叫Render方法,會導致BuildManagerCompiledViewRazorView兩個類的Render方法被呼叫。最後在RazorViewRenderView方法裡面呼叫 webViewPage.ExecutePageHierarchy(new WebPageContext(context: viewContext.HttpContext, page: null, model: null), writer, startPage);

會呼叫WebViewPage裡面的ExectuePageHierarchy方法,因為webViewPage裡面沒有三個過載的ExectuePageHierarchy方法,會轉到WebViewPage的基類WebPageBase執行它的ExectuePageHierarchy方法。此方法裡面有三個動作,入棧(push,執行頁面(startpage.Execute,出站(pop.

 

入棧會把當前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方法。ExectuePageHierarchyBase.ExectuePageHierarchy.當前頁面一般會被編譯成WebViewPage的派生類。WebViewPage又派生自WebPageBase。所以Base.ExectuePageHierarchy.會呼叫webPageBase裡面的ExectuePageHierarchy.

 

 

Execute呼叫的是已經編譯好的CSHTML頁面。例如控制器為Account,檢視Login,那麼MVC會把它編譯成:名稱為_Page_Views_Account_Login_Cshtml的類,這個類繼承自WebViewPage<TModel>WebViewPage<TModel>又繼承自WebViewPageWebViewPage則繼承自WebPageBase


WebPageBase.ExectuePageHierarchy兩個引數方法呼叫了過載的三個引數方法,添加了一個StartPagenull.重複上面的動作,入棧,因為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.