《Pro ASP.NET MVC 3 Framework》学习笔记之二十七【视图1】
在前面很多的章節(jié)里面的,最常用的action result是視圖呈現(xiàn)并返回給客戶端的ViewResult類型。本章會專注于視圖的原理,首先展示MVC框架是如何使用視圖引擎處理ViewResults的,包括闡釋如何創(chuàng)建一個視圖引擎。接著介紹使用Razor視圖引擎的一些技術(shù)。最后是關(guān)于創(chuàng)建和使用部分視圖,子actions,以及Razor片段,這些都是涉及高效MVC開發(fā)的本質(zhì)話題。
創(chuàng)建一個自定義視圖引擎(Creating a Custom View Engine)
MVC框架包含了兩個內(nèi)置的,功能完善,容易理解的視圖引擎:
a.Razor引擎:MVC3里面引入的一個新的視圖引擎,具有更加簡潔和優(yōu)雅的語法。
b.ASPX引擎:使用的是webform里面的標(biāo)簽語法"<%..%>",用于維護兼容舊的MVC程序。
這里創(chuàng)建一個自定義的視圖引擎的目的是為了闡釋請求管道的運行原理以及完善我們對MVC框架運行原理的認識,包含了理解視圖引擎在轉(zhuǎn)化一個ViewResult到響應(yīng)客戶端的輸出有多少自主性。
視圖引擎實現(xiàn)了IViewEngine接口,如下所示:
View Code視圖引擎扮演的角色是將請求轉(zhuǎn)換成ViewEngineResult對象,前面兩個方法包含的參數(shù)分別是:描述請求,處理請求的控制器,視圖及其布局,以及是否允許從它的緩存里面取出前一次的結(jié)果。這些方法在ViewResult被處理時調(diào)用。最后一個方法當(dāng)視圖不在需要時被調(diào)用。
注:MVC框架對視圖引擎的支持是通過ControllerActionInvoker類實現(xiàn)的,這個類是IActionInvoker接口的實現(xiàn)。如果我們實現(xiàn)的是自己的action調(diào)用者或控制器工廠的話,這樣是不會自動訪問視圖引擎的功能。
ViewEngineResult類允許視圖引擎在一個視圖被請求時響應(yīng)給MVC框架,我們通過選擇兩個構(gòu)造器中的一個來表達結(jié)果。
如果視圖引擎能夠提供一個針對請求的視圖,那么我們可以使用的構(gòu)造器是:public ViewEngineResult(IView view, IViewEngine viewEngine)?
否則,能使用的構(gòu)造器為:public ViewEngineResult(IEnumerable<string> searchedLocations) ,參數(shù)提供能夠找到視圖的位置。
視圖引擎系統(tǒng)最后一塊是IView接口,如下:
我們傳遞一個IView接口的實現(xiàn)給ViewEngineResult對象的構(gòu)造器,然后從視圖引擎的方法返回。MVC框架調(diào)用Render方法,ViewContext參數(shù)包含了關(guān)于請求的信息和action方法的輸出。TextWriter參數(shù)將輸出寫到客戶端。下面會創(chuàng)建一個視圖引擎,只返回一種視圖,該視圖會呈現(xiàn)包含請求信息的結(jié)果以及由action方法產(chǎn)生的視圖數(shù)據(jù)。從創(chuàng)建視圖引擎的過程里面能讓我們了解視圖引擎運作的方式,這里是沒有涉及到解析視圖模版的。
創(chuàng)建一個自定義的IView接口(Creating a Custom IView)
View Code創(chuàng)建IViewEngine實現(xiàn)(Creating an IViewEngine Implementation)
我要明確視圖引擎的目的是生產(chǎn)一個ViewEngineResult對象,該對象包含一個IView接口或者是一個包含視圖位置的列表。上面創(chuàng)建了IView接口,下面接著創(chuàng)建視圖引擎,如下:
View Code這里創(chuàng)建的視圖引擎支持DebugData視圖,當(dāng)有一個針對該視圖的請求時,則返回一個IView接口實現(xiàn)的實例,像這樣:return new ViewEngineResult(new DebugDataView(), this);
如果我們要實現(xiàn)更加正式的視圖引擎,需要利用這個時機尋找模版,并考慮布局和提供緩存設(shè)置。這里的例子僅僅需要創(chuàng)建一個DataDebugView類的實例,如果接收到其他的請求,會返回如:return new ViewEngineResult(new string[] { "Debug Data View Engine" }); IViewEngine接口假定視圖引擎具有它需要用來尋找視圖的位置信息,這是非常合理的假設(shè),因為視圖是典型的存儲在項目里的模版文件。這里并不需要去找,我們返回是一個虛擬的位置。這里也沒有支持部分視圖,也沒有實現(xiàn)ReleaseView方法,因為沒有資源需要釋放。
注冊自定義的視圖引擎(Registering a Custom View Engine)
注冊視圖引擎的方法有很多,首選的是在Global的Application_Start方法里面添加 ViewEngines.Engines.Add(new DebugDataViewEngine());?
靜態(tài)的ViewEngine.Engines集合包含了一套安裝在應(yīng)用程序的視圖引擎。當(dāng)一個ViewResult正被處理時,action調(diào)用者獲得已經(jīng)安裝的視圖引擎的集合并輪流調(diào)用它們的FindView方法。當(dāng)action調(diào)用者收到一個包含IView接口的ViewEngineResult對象時停止調(diào)用FindView方法。這意味著添加到ViewEngines.Engines里面的引擎的順序非常重要,特別是在有多個引擎服務(wù)同名的視圖時。
如果想要我們的視圖引擎優(yōu)先,可以利用集合的插入方法如下:
ViewEngines.Engines.Insert(0, new DebugDataViewEngine());?
還有一種方法是使用全局依賴解析器,當(dāng)應(yīng)用程序啟動時,依賴解析器會請求任何可用的IViewEngine接口實現(xiàn)。如果使用NinjectDependencyResolver類,我們可以在AddBinds方法里注冊視圖引擎,
如:private void AddBindings()
{?
??? //綁定
??? Bind<IViewEngine>().To<DebugDataViewEngine>();? ?
}
使用這種方式不能控制視圖引擎被處理的順序,如果我們要關(guān)注視圖引擎會相互競爭,最好還是選擇ViewEngines.Engines集合里面的Insert方法來實現(xiàn)。下面測試我們自定義的視圖引擎,創(chuàng)建一個HomeController類如下:
View Code這時可以運行程序,結(jié)果如下:
出現(xiàn)上面的結(jié)果是因為針對我們處理的視圖的FindView被調(diào)用了。如果我們改變下Index方法以至于ViewResult請求一個視圖時沒有安裝的引擎能夠響應(yīng),如下:
return View("No_Such_View"); 這時我們運行程序,action調(diào)用者會去遍歷每一個可用的視圖引擎并調(diào)用它們的FindView方法。沒有一個能夠服務(wù)這個請求,并且會返回它們尋找過位置集合。如圖:
使用Razor引擎(Working with the Razor Engine)
在前面我們通過實現(xiàn)兩個接口創(chuàng)建了自定義的視圖引擎,當(dāng)然是非常簡單的,只是為了說明其原理。視圖引擎的復(fù)雜性來自于視圖模版系統(tǒng),包含了代碼分片,支持布局,可編譯來優(yōu)化性能。這些在我們自定義的視圖引擎里面是沒有的,也不需要,因為Razor引擎考慮了所有的。
理解Razor視圖渲染(Understanding Razor View Rendering)
Razor視圖引擎通過編譯視圖來改善性能,視圖會被轉(zhuǎn)變?yōu)镃#類,然后編譯。這也是為什么我們能夠在視圖里面包含C#代碼片段的原因。看下Razor視圖創(chuàng)建的源碼會非常有益的,因為它幫助我們放置了很多Razor功能在上下文Context里面。下面是一個簡單的Razor視圖,獲取了一個字符串?dāng)?shù)組作為視圖模型對象:
View Code在應(yīng)用程序啟動時,視圖才會被編譯,也只有在這個時候才能看到由Razor創(chuàng)建的C#類,我們需要啟動應(yīng)用程序并導(dǎo)航到一個action方法。然后一個actin方法都行,因為初始化請求時會觸發(fā)視圖編譯過程。非常方便的是,創(chuàng)建的C#類會作為C#代碼文件寫入磁盤然后編譯,這意味著我們能夠查看到C#具體的代碼。如果是Windows7系統(tǒng),查看路徑為:c:\Users\yourLoginName\AppData\Local\Temp\Temporary ASP.NET Files。下面是一個示例文件:
View Code首先,可以注意到這個類是從WebViewPage<T>派生的,T表示model的類型,這也是能夠處理強類型視圖的原因。也可以發(fā)現(xiàn)視圖文件的路徑是怎樣被編碼為類名的。這也是Razor映射視圖的請求到已編譯的類的實例的原理。在Execute方法里,我們能夠看到視圖里面語句和元素是怎樣被處理的。HTML元素被WirteLiteral方法處理,這個方法可以將參數(shù)的內(nèi)容寫到結(jié)果里面。這剛好跟Write方法相反,Write方法對C#變量使用并對字符串的值編碼讓它們能夠在HTML頁面安全使用。
WriteLiteral和Write方法都是將內(nèi)容寫到一個TextWriter對象里面。這個對象同樣被傳遞給了IView.Render方法。一個已經(jīng)編譯的Razor視圖的目的是為了創(chuàng)建靜態(tài)和動態(tài)的內(nèi)容并通過TextWriter發(fā)送給客戶端。
添加依賴注入到Razor視圖(Adding Dependency Injection to Razor Views)
MVC請求處理管道的每一個部分都支持DI(Dependency Injection),Razor也不例外。然而所不同的是,這里擴展了類和視圖之間的邊界。下面是一個例子來說明:
創(chuàng)建一個接口ICalculator如下:
如果我們想要一個視圖能夠使用ICalculator并且具有注入的實現(xiàn),那么我們需要創(chuàng)建一個派生自WebViewPage的抽象類,如下:
View Code支持DI最簡便的方式就是使用屬性注入(property injection),即:我們的DI容器注入依賴到一個屬性里面,而不是最開始時使用的構(gòu)造器注入。這里使用的了Inject特性,為了在視圖里面利用它,我們使用Razor繼承元素,如下所示:
View Code我們能夠使用@inherits指定基類,這樣就能夠訪問Calculator屬性并接收ICalculator接口的實現(xiàn),所有這一切都沒有創(chuàng)建view和ICalculator接口之間的依賴。使用了@inherits元素的效果就是改變了實體編譯創(chuàng)建的類的基類,如:public class _Page_Views_Home_Calculate_cshtml : Views.Models.ViewClasses.CalculatorView {...}
由于創(chuàng)建的類是從我們的抽象類派生的,所以能夠訪問我們自己定義的Calculator屬性。剩下的就是使用依賴解析器注冊ICalculator接口實現(xiàn),以至于Ninject將被用來創(chuàng)建視圖類并且有機會實施屬性注入。如下:
這時可以運行程序得到如下結(jié)果:
配置視圖搜索位置(Configuring the View Search Locations)
Razor視圖引擎遵循早期MVC版本里面尋找視圖的建立的約定,如果請求與Home控制器關(guān)聯(lián)的Index視圖,Razor按照下面的列表尋找:
?? ~/Views/Home/Index.cshtml?
?? ~/Views/Home/Index.vbhtml?
?? ~/Views/Shared/Index.cshtml?
?? ~/Views/Shared/Index.vbhtml
Razor不是真的在磁盤上尋找視圖文件,因為視圖這個時候已經(jīng)編譯成了C#類。Razor實際上是尋找的這些代表視圖的編譯后的C#類,.cshtml文件是包含C#語句的模版。我們可以通過創(chuàng)建RazorViewEngine的子類來改變Razor搜尋的視圖文件。它是建立在一序列的基類之上的,這些基類包含了決定哪一個視圖文件被搜尋的一套屬性。如下:
占位符{0},{1},{2}分別表示:視圖名,控制器名,區(qū)域名。
為了改變搜尋的位置,我們創(chuàng)建一個從RazorViewEngine派生的類并改變屬性的值,下面是一個示例:
代碼里面我們?yōu)閂iewLocationFormats設(shè)置了新的值,新的數(shù)組包含了全部的.cshtml文件。另外,還改變了公共視圖的尋找位置,不再是Views/Shared而是Views/Common。接著需要在Application_Start方法里面注冊:
View Code記住一點:action調(diào)用者輪流去每一個視圖引擎尋找視圖,等到我們添加視圖到集合里面時,它已經(jīng)包含了標(biāo)準(zhǔn)的Razor視圖引擎。為了避免跟其他的視圖引擎競爭,我們調(diào)用Clear方法移除任何可能已經(jīng)注冊的視圖引擎并調(diào)用Add方法注冊我們自己實現(xiàn)的視圖引擎。
添加動態(tài)的內(nèi)容到Razor視圖(Adding Dynamic Content to a Razor View)
視圖的全部意圖就是允許我們將領(lǐng)域模型作為用戶接口呈現(xiàn)出來。為了達到這個目的,我們需要添加動態(tài)的內(nèi)容,動態(tài)內(nèi)容是在運行時創(chuàng)建的,并且每一次請求創(chuàng)建的內(nèi)容都可以不同。這個跟靜態(tài)內(nèi)容是相反的,像HTML這些靜態(tài)的內(nèi)容在每次請求時都是一樣的。添加動態(tài)內(nèi)容的方式有四種:內(nèi)嵌代碼(Inline code),HTML輔助方法(HTML helper methods),部分視圖(Partial views),子action(Child actions).下面分別對每一種方式進行介紹:
使用內(nèi)嵌代碼(Using Inline Code)
創(chuàng)建動態(tài)內(nèi)容最簡便的方式就是使用內(nèi)嵌代碼——一行或多行以@符合開始的C#代碼。這是Razor視圖引擎的核心。
內(nèi)嵌代碼與分解關(guān)注點:學(xué)過ASP.NET WebForms的人可能非常想知道為什么會對內(nèi)嵌代碼如此著迷,畢竟在WebForms里面的約定是盡可能的將代碼放在代碼隱藏文件里面。這很容易混淆,特別是當(dāng)我們認為代碼隱藏文件常常是對分解關(guān)注點的維護時。這個分歧的出現(xiàn)是因為MVC和WebForms對哪一個關(guān)注點應(yīng)該被分解以及分界線應(yīng)該在哪有不同的概念。WebForms將陳述性的HTML標(biāo)記和程序邏輯分開,ASPX包含HTML標(biāo)簽,代碼隱藏文件包含邏輯。與之相對的是MVC,它是將展示邏輯和應(yīng)用邏輯分開,控制器和領(lǐng)域模型分別負責(zé)應(yīng)用邏輯和領(lǐng)域邏輯,視圖包含展示邏輯。所以內(nèi)嵌代碼更好的適應(yīng)了MVC框架。Razor句法讓我們很容易的創(chuàng)建可維護,可擴展的視圖。
內(nèi)嵌代碼功能的靈活性很容易模糊應(yīng)用程序的邊界,并且讓分解關(guān)注點的思想瓦解。作為一種紀律約束:必須通過某種方式來維護分解關(guān)注點的有效性,那就是與MVC設(shè)計模式保持一致。當(dāng)我們著手使用MVC框架時,建議將關(guān)注點放在理解功能上。當(dāng)我們有了更多的經(jīng)驗時,會自然形成將視圖和MVC組件的職責(zé)分開的感覺。
好了,今天的筆記就到這里,關(guān)于這章的筆記在下一篇接著做完。
晚安!
本文轉(zhuǎn)自Rt-張雪飛博客園博客,原文鏈接http://www.cnblogs.com/mszhangxuefei/archive/2012/04/06/mvcnotes_27.html如需轉(zhuǎn)載請自行聯(lián)系原作者
張雪飛
總結(jié)
以上是生活随笔為你收集整理的《Pro ASP.NET MVC 3 Framework》学习笔记之二十七【视图1】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 无盘中用户数据的保存
- 下一篇: 局域网IP-MAC绑定方案