Agenda
前言
上一篇說到最終會透過一個實現IView物件(Razor是透過RazorView)來完成,RenderView方法將BuildManagerCompiledView方法取得物件轉換型別成WebViewPage.
.cshtml最終會編譯成一個繼承WebViewPage檔案.
本篇會來解析View編譯原理
我有做一個可以針對於Asp.net MVC Debugger的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.
WebViewPage
WebViewPage繼承樹最頂層有個WebPageExecutingBase抽象類別,他擁有一個抽象方法Execute,View轉成c#程式會建立一個類別就會繼承於WebViewPage並把使用者頁面程式碼實現在Execute方法.
1 | public abstract void Execute(); |
先來看一下View產生的DLL檔案會放在哪裡
透過在View檔案上寫@GetType().Assembly.Location.
在頁面上顯示DLL存放位置,一般會放在Temp資料夾區中
可以根據顯示路徑找到View編譯成DLL
1 | public class _Page_Views_Shared__Layout_cshtml : WebViewPage<object> |
我使用JustDecomplie反編譯工具,查看原始碼.
下圖對於View檔案產生DLL反編譯
透過反編譯工具可以看到原始碼,每個頁面都會產生相對應的類別並繼承於WebViewPage<object>類別(會因為使用泛型,因為有一個@Model)
Page_Views_Home_About_cshtml類別命名有個規則.Page_Views_{ViewfolderName}_{ViewFileName}_{ExtensionFileName}
我目前看到的是一個About的cshtml檔案(About.cshtml).
看到override void Execute()將我們頁面上的邏輯透過WriteLiteral將資料寫到Output上,在ApplicationStartPage有WriteLiteral實作方式.
1 | public override void WriteLiteral(object value) |
呼叫WebViewPage.ExecutePageHierarchy方法時機
在RazorView類別中的RenderView方法最下面有一段程式碼.
先判斷是否取得StartPage在呼叫ExecutePageHierarchy方法進行頁面的渲染.
1 | WebPageRenderingBase startPage = null; |
ApplicationStartPage and WebPageRenderingBase
WebViewPage類別關係圖如下,WebViewPage擁有個複雜繼承樹.

主要分為兩派,在微軟官網有張圖來表示上面兩個比較

ApplicationStartPage:當站台被啟動時會使用WebPageHttpModule(IHttpModule)初始化並呼叫ApplicationStartPage的ExecuteStartPageInternal找尋_appstart.cshtml檔案來執行.ASP.NET Web Pages (Razor) 網站的自訂全網站行為
_AppStart.cshtml頁面上運作。 當要求傳入頁面中,和如果這是第一個要求任何頁面在網站中,ASP.NET會先檢查是否_AppStart.cshtml頁面存在。 如果是的話,任何程式碼中_AppStart.cshtml頁面上執行,並執行要求的頁面。
WebPageRenderingBase:透過ExecutePageHierarchy呼叫BaseLayout頁面或執行請求Execute方法
WebPageBase類別中ExecutePageHierarchy重載實作,透過ExecutePageHierarchy呼叫開發者實現Execute方法(Page_Views_Home_About_cshtml.Execute方法).
1 | public override void ExecutePageHierarchy() |
WebViewPage vs WebViewPage
c# 有一個關鍵字new對於類別成員修飾詞
new關鍵字做為宣告修飾詞使用時,會明確隱藏繼承自基底類別的成員。當您隱藏繼承的成員時,該成員的衍生版本就會取代基底類別版本
我覺得這個關鍵字有點打壞物件導向的概念,因為他會把父類別原本的成員隱藏起來.強制替換成子類.
但我看到WebViewPage<TModel>實作時覺得new原來可以這麼好用
WebViewPage<TModel>很巧妙使用new把View重點成員物件轉成泛型.可以讓我們在Razor或aspx可以更方便使用.
1 | public abstract class WebViewPage<TModel> : WebViewPage |
所以之後如果有遇到類似情況(需要使用泛型替代父類別object類型成員可以考慮使用new)
小結:
View頁面程式會轉成一個類別繼承於WebViewPage抽象類別,並把我們撰寫邏輯填充在Execute方法中.讓Asp.net MVC來呼叫.
這裡設計非常巧妙透過一個抽象類別和一個動態編譯程式,讓View更有彈性可以透過Razor語法實現View邏輯(更人性化).
WebViewPage<TModel>很巧妙使用new把View重點成員物件轉成泛型.可以讓我們在Razor或aspx可以更方便使用.
最後透過呼叫ActionResult.ExecuteResult方法將資料塞到Response物件中,提供回傳給Client端,最後執行資源Release動作.
後面幾篇會利用前面所學來改寫MVC框架.
__此文作者__:Daniel Shih(石頭)
__此文地址__: https://isdaniel.github.io/Ithelp-day25/
__版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!