Agenda
前言
繼承ActiontResult
類別中ViewResultBase
最為複雜,因為ViewResultBase
要找到實現IViewEngine
物件取得取得View
檔案,在透過實現IView
物件把頁面渲染出來.
這篇會跟大家分享值型上面動作核心類別.
個人覺得MVC運用很多物件導向概念和用法,在讀程式時有件事情很重要是理解類別負責的工作和類別之間關係.就像現實生活中人與人的關係要了解清楚.
我有做一個可以針對於Asp.net MVC Debugger的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.
ViewResultBase.ExecuteResult
因為ExecuteResult
是最終被呼叫方法,我們來解析ViewResultBase.ExecuteResult
方法邏輯.
- 透過子類別實現
FindView
取得View
相關資料. - 呼叫實現
IView
物件Render
方法,並將渲染出來資料透過Response.Output
輸出到Client
端
1 | public override void ExecuteResult(ControllerContext context) |
這張UML
表示ViewResultBase
繼承關係圖.
我們在Controller
呼叫的View()
和PartailView()
方法就是建立PartialViewResult
和ViewResult
方法並且呼叫ExecuteResult
進行View
頁面渲染.
IView
View是一個實現了IView
介面物件。IView
定義非常簡單,僅僅具有唯一Render
方法根據指定ViewContext
和TextWriter
物件達成對於View
渲染顯示
1 | public interface IView |
BuildManagerCompiledView
BuildManagerCompiledView
類別實現Render
對於View
如何被渲染呈現.
主要透過下面幾個步驟.
.cshtml,.aspx
頁面程式碼會轉成編譯成一個繼承WebViewPage
類別的dll
檔案.BuildManagerWrapper
靜態方法GetCompiledType
依據指定View
檔案虛擬路徑得到編譯後WebPageView
類型IViewPageActivator(DefaultViewPageActivator)
利用反射建立WebPageView
物件由頁面程式產生的View
物件- 最後再呼叫由子類實現
RenderView
方法
BuildManagerCompiledView
屬性ViewPath
表示的就是View
文件虛擬路徑.
1 | public abstract class BuildManagerCompiledView : IView |
RazorView
RazorView
繼承BuildManagerCompiledView
,RazorView
具有三個只讀屬性
LayoutPath
:View
佈局檔案虛擬路徑ViewStartFileExtensions
:表示開始頁面文件的擴展名,對於Razor
引擎默認創建RazorView
,通過_ViewStart.cshtml
檔案定義開始頁面相關資訊.RunViewStartPages
:這個bool
掌控執行開始頁面判斷1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49public class RazorView : BuildManagerCompiledView
{
public string LayoutPath { get; private set; }
public bool RunViewStartPages { get; private set; }
public IEnumerable<string> ViewStartFileExtensions { get; private set; }
protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
WebViewPage webViewPage = instance as WebViewPage;
if (webViewPage == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.CshtmlView_WrongViewBase,
ViewPath));
}
webViewPage.OverridenLayoutPath = LayoutPath;
webViewPage.VirtualPath = ViewPath;
webViewPage.ViewContext = viewContext;
webViewPage.ViewData = viewContext.ViewData;
webViewPage.InitHelpers();
if (VirtualPathFactory != null)
{
webViewPage.VirtualPathFactory = VirtualPathFactory;
}
if (DisplayModeProvider != null)
{
webViewPage.DisplayModeProvider = DisplayModeProvider;
}
WebPageRenderingBase startPage = null;
if (RunViewStartPages)
{
startPage = StartPageLookup(webViewPage, RazorViewEngine.ViewStartFileName, ViewStartFileExtensions);
}
webViewPage.ExecutePageHierarchy(new WebPageContext(context: viewContext.HttpContext, page: null, model: null), writer, startPage);
}
}
RenderView
方法執行幾個步驟.
RenderView
方法將BuildManagerCompiledView
方法取得instance
物件轉換型別成WebViewPage
- 資料初始化(建立
UrlHelp
,….)物件 - 判斷是否使用
Razor
共用樣板 - 呼叫
ExecutePageHierarchy
,進行頁面渲染,最主要呼叫Execute
方法來執行子類別實現邏輯.
下面是IView
類別關係圖
最後由WebFormView
,RazorView
實現頁面的渲染工作.
IViewEngine
這個介面提供找尋使用ViewEngineResult
,View
和ViewEngine
屬性找到View
物件和使用的ViewEngine
物件,SearchedLocations
屬性表示在獲取目標搜索過程中使用的搜索位置列表
1 | public interface IViewEngine |
VirtualPathProviderViewEngine
VirtualPathProviderViewEngine
這個抽象類別,實現FindPartialView
和FindView
方法,另外提供一個抽象方法CreateView
和CreatePartialView
提供子類(WebFormViewEngine
,RazorViewEngine
)來實現.
下面是FindView
原始碼.
1 | public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) |
RazorViewEngine
前面有提到VirtualPathProviderViewEngine
提供一個抽象類別給子類來實現如何建立一個IView
物件.
RazorViewEngine
透過上面資訊建立一個RazorView
(此類別實現IView
介面),最終ViewBaseResult
就是呼叫IView
的Render
方法.
RazorViewEngine
就是建立到時候要Render
到OutputStream
物件.
1 | protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) |
ViewEngines and ViewEngineCollection
透過ViewEngines.Engines
可以取得目前可以使用View
引擎.
ASP.NET MVC為我們提供了兩種View
引擎(RazorViewEngine
,WebFormViewEngine
),
- 提供傳統Web Form引擎,
.aspx
頁面一致WebFormViewEngine
, - 另一種預設使用也是推薦使用Razor引擎
RazorViewEngine
。
1 | public static class ViewEngines |
ViewEngine
類別關係圖如下
這邊以RazorViewEngine
來介紹
ViewLocationFormats
:預設找尋View
實體檔案位置PartialViewLocationFormats
:預設找尋PartialView
實體檔案位置FileExtensions
:Razor
使用附檔名.
提升執行效率小技巧
這裡有個小技巧可提高MVC執行效率.
移除不必要ViewEngine提升執行效率
MVC藉由ViewEngineCollection
這個集合來判斷使用ViewEngine
,且它預設有兩個ViewEngines
提供給我們使用(RazorViewEngine
,WebFormViewEngine
)一般來說我們只使用一個ViewEngine
另一個就不會用到.
如果我們只使用RazorViewEngine
就可在Global.cs
上撰寫這段程式碼,主要是把不必要ViewEngine
移除只關注在我們使用ViewEngine
1 | ViewEngines.Engines.Clear(); |
只允許某個View副檔名
在Razor
有支援兩個副檔名
vbhtml
:vb使用cshtml
:c#使用
如果我們想強制這個專案都使用C#的Razor撰寫view,可藉由幾個屬性來幫我們限制完成.
1 | ViewEngines.Engines.Add(new RazorViewEngine() |
小結:
本篇大致上把產生View
頁面使用到的幾個核心介面和類別介紹完了,我們主要會使用繼承ViewResultBase
物件並透過,相對應實現IView
物件來進行畫面渲染,如何取得使用的IView
物件就透過ViewEngines
集合.
上面介紹了三個抽象類別和介面,每個都有自己核心職責並且和其他物件有清晰關係
ViewResultBase
:實現ActionResult
提供Controller
呼叫產生頁面ExecuteResult
方法.IView
:提供如何渲染頁面IViewEngine
:透過虛擬路徑找到要執行頁面(透過一些機制).
此文作者:Daniel Shih(石頭)
此文地址: https://isdaniel.github.io/ithelp-day24/
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!