ASP.NET MVC框架(第一部分)

  • 23617
  • 0
  • 2011-07-09

摘要:ASP.NET MVC框架(第一部分)

【原文位址】ASP.NET MVC Framework (Part 1)
【原文發表日期】 Tuesday, November 13, 2007 3:45 AM

兩個星期前, 我在部落格裡討論了ASP.NET的一個新MVC(模型、檢視,控制器)框架,我們將在不久的將來作為一個可選功能來支援。該框架提供了一個結構化的模型,來加強應用中的清晰關注分離,方便你單元測試程式碼和支援TDD流程。它還提供了對你在應用中發佈的URL的更多的控制,也可以對從中輸出的HTML提供更多的控制。

之後,我回答了來自迫切想瞭解更多詳情的很多人的很多問題。鑒於如此高的興趣,我覺得,寫幾個文章更詳細地描述如何使用這個框架,也許更有意義些。這是我將在以後幾個星期裡要撰寫的相關文章的第一個。

一個簡單的電子商務店面應用

我將使用一個簡單的電子商務商店應用來示範ASP.NET MVC框架的工作原理。在今天的文章裡,我將實現一個產品列單,以及相關的瀏覽應用場景。

具體來說,我們將建造一個網上商店,允許用戶在存取該網站上的/Products/Categories網址時 瀏覽產品分類列表:

當用戶點擊上面網頁上的產品分類連結時,他們將轉到一個產品分類列表URL /Products/List/CategoryName上,該頁面列出了指定分類中的還在銷售的產品:

當用戶點擊個別的產品時,他們將轉到產品細節URL /Products/Detail/ProductID上,這個網頁將顯示用戶選定的產品的更多細節:

我們將使用新的ASP.NET MVC框架來實現上述的所有功能。這將會允許我們在應用的不同元件間保持「清晰的關注分離」,允許我們更輕易地整合單元測試和測試驅動的開發。

建立一個新的ASP.NET MVC應用

ASP.NET MVC框架包含一個Visual Studio專案模板,方便你建立新的MVC web應用。選擇檔案->新專案選單,選擇「ASP.NET MVC Web 應用」模板,用它建立一個新web應用。

在預設情形下,當你使用該選項生成一個新應用時,Visual Studio 將為你建立一個新的解決方案,然後往裡面加2個專案。第一個專案是web專案,在其中你實現你的web應用的功能。第二個專案是個測試專案,你可以在其中編寫單元測試,來測試你的應用程式碼:

你可以在ASP.NET MVC 框架中使用任何單元測試框架,包括NUnit, MBUnit, MSTest, XUnit以及其他的框架。VS 2008專業版現在包含了對MSTest的內建測試專案的支援(VS 2005版本的MSTest要求你擁有Visual Studio Team System版本才能使用),當你使用VS 2008時,預設的ASP.NET MVC 專案模組自動生成這樣的測試專案。

我們還將發佈可用以NUnit, MBUnit 和其他單元測試框架的專案模板,所以,如果你更喜歡那些框架的話,你可以輕鬆地一次點擊即生成你的應用和可以馬上使用的相應的測試專案。

理解專案的目錄結構

ASP.NET MVC 應用的預設目錄結構有三個頂層目錄:

  • /Controllers
  • /Models
  • /Views

你大概可以猜出來,我們建議把控制器類置於 /Controllers 目錄之中,你的資料模型類置於/Models目錄之中,你的檢視模板置於 /Views 目錄之中。

雖然ASP.NET MVC框架並不強迫你總是使用這個結構,但預設的專案模板使用這個模式,我們也把它作為結構化應用的一種比較容易的方式向你推薦。除非你有好的理由使用另外的檔案佈局,我建議你使用這個預設模式。

把URL映射到Controller類別

在大多數web框架(ASP, PHP, JSP, ASP.NET WebForms等等)裡,到來的URL一般都映射到保存在硬碟上的模板檔案。譬如,」/Products.aspx」或者」/Products.php」 URL一般都在硬碟上有個對應的Products.aspx 或Products.php 模板檔案來處理請求。當一個web應用的http請求進入web伺服器時,web框架運行由硬碟上的模板檔案指定的程式碼,然後這程式碼負責處理該請求。很多時候,這程式碼使用Products.aspx 或 Products.php檔案中的HTML 標識來幫助生成傳回客戶端的回應。

MVC框架一般以不同的方式把URL映射到伺服器程式碼上。它不是將URL映射到硬碟上的模板檔案,而是直接把URL映射到程式碼類別上。這些類稱為「Controllers(控制器)」,它們負責處理到來的請求,處理用戶輸入和交互,執行基於輸入和交互的相應的應用和資料邏輯。然後,一個Controller類別一般會叫用單獨的「檢視」元件,該元件負責生成請求的實際的HTML輸出。

ASP.NET MVC框架包括一個非常強大的URL映射引擎,在如何把URL映射到Controller類別方面,該引擎提供了很多彈性。你可以使用它來輕鬆地設置routing(路由)規則,然後ASP.NET會根據這些規則,對進來的URL進行評估,選出一個Controller來運行。然後你也可以讓routing引擎自動分析出你在URL裡定義的變數,讓ASP.NET自動把這些變數作為參數傳給你的Controller。我將在這個系列將來的一個文章裡,討論涉及URL routing引擎的比較高級的場景。

映射到控制器類的預設ASP.NET MVC URL Routing規則

在預設情形下,ASP.NET MVC專案有一套預先配置好的URL routing規則,這些規則允許你不用配置什麼,就可以輕鬆地上路。這樣,使用一套預設的基於名稱的URL映射約定,你就可以開始編寫程式碼了,這些約定是在Global.asax檔案(由Visual Studio中新的ASP.NET MVC專案模板生成的)中的ASP.NET Application類別中宣告的。

預設的命名約定是這樣的:把進來的HTTP請求的URL路徑的開頭部分,譬如 /Products/,映射到一個類,該類別的名稱遵循UrlPathController的模式,譬如在預設情形下,一個以/Products/開頭的URL 會被映射到名為ProductsController的類別上。

為建造我們的電子商務產品瀏覽功能,我們將在我們的專案中加一個新的「ProductsController」類別(你可以使用Visual Studio中的「添加新項」選單從模板中輕鬆地建立一個Controller類別):

我們的ProductsController是從System.Web.MVC.Controller 基礎類別繼承而來,從這個基礎類別繼承而來並不是必需的,但它含有一些我們以後可以使用的非常有用的輔助方法和功能:

在專案中定義這個ProductsController類別之後,在預設情形下,ASP.NET MVC 框架就會使用它來處理所有到來的以」/Products/」開頭的URL的應用請求。這意味著,它會自動被叫用來處理我們將在我們的網上商店應用中開啟的」/Products/Categories」, 「/Products/List/Beverages」, 和 「/Products/Detail/3〞 等URL。

在將來的文章裡,我們還將添加一個ShoppingCartController(以允許用戶管理他們的購物車)以及 AccountController (允許用戶在網站上建立新的成員帳號,實現登錄和退出等功能)。在向我們的專案裡添加這2個新的控制器類別之後,以/ShoppingCart/ 和 /Account/開頭的URL就會自動地導向到這些類別做處理。

註:ASP.NET MVC框架並不要求你總是使用這個命名約定模式。我們的應用預設使用這個模式的唯一原因是因為在我們使用Visual Studio建立新的ASP.NET MVC專案時,有一個配置了這個模式的映射規則被自動地添加到了我們的ASP.NET Application 類中。如果你不喜歡這個規則,或者想使用另外的URL映射模式來對它進行客制,那麼就到Global.asax中的ASP.NET Application類中做改動。我會在以後的一個文章討論該怎麼做,到時我也會展示一些URL routing引擎允許的一些非常酷的場景。

理解控制器的Action方法

既然我們已經在專案裡建立了一個ProductsController類,我們可以開始添加邏輯來處理來到應用的」/Products/」 URL了。

在本文章的前面定義我們的電子商務店面用例時,我說過我們將在網站上實現3個場景:1) 瀏覽所有的產品分類, 2) 列出特定分類裡的產品, 和 3) 顯示特定產品的細節。我們將使用下列這些SEO友好的URL來處理這三個場景:

URL格式 行為 URL例子
/Products/Categories 瀏覽所有的產品分類 /Products/Categories
/Products/List/Category 列出特定分類裡的產品 /Products/List/Beverages
/Products/Detail/ProductID 顯示特定產品的細節 /Products/Detail/34

我們可以用幾種方式在ProductsController類中編寫程式碼來處理這三類到來的URL。一種方式是,覆蓋Controller基礎類別中的「Execute」方法,手工編寫我們自己的 if/else/切換邏輯,對照用戶請求的URL,然後執行適當的邏輯來處理這個請求。

但一種容易得多的方式是,使用MVC框架中內建的功能,該功能允許我們在我們的控制器中定義「action方法」,然後由Controller基礎類別根據我們應用使用的URL routing規則來自動叫用適當的action方法來執行。

譬如,我們可以往我們的ProductsController類別裡添加如下所示的三個控制器action方法,來處理上述的三個電子商務URL場景:

在專案建立時預設配置的URL routing規則會把緊隨控制器名稱之後的子路徑當作請求的action名稱來對待。所以,如果我們收到一個/Products/Categories的URL請求,routing規則會把「Categories」作為一個action的名稱來對待,Categories() 方法就會被叫用來處理這個請求。如果我們收到的是 /Products/Detail/5 URL請求,routing規則就會把「Detail」」當中action的名稱,Detail() 方法就會被叫用來處理請求,等等。

註:ASP.NET MVC框架並不要求你總是使用這個action命名約定模式。如果你想使用不同的URL映射模式,到 Global.asax檔案中的ASP.NET Application類別去做改動。

把URL參數映射到Controller的Action方法上

在Controller類中的action方法中可以用幾個方法存取URL中的參數值。

Controller基礎類別呈現了可以使用的Request 和Response物件。這些物件跟ASP.NET中你已經熟悉的HttpRequest/HttpResponse物件擁有完全一樣的API結構。一個非常重要的區別是,這些物件現在是基於介面(interface)的,而不是封閉的類別。具體來說,MVC 框架將發佈System.Web.IHttpRequest和System.Web.IHttpResponse介面。這些物件是基於的介面的好處是,現在非常容易mock(模仿)它們,這樣,就可以方便對控制器類別的單元測試。在將來的部落格文章裡,我將對此進行深入的討論。

下面是一個如何在ProductsController類的Detail action方法中使用Request API來手工獲取ID查詢字串值的例子:

ASP.NET MVC 框架還支援自動將進來的URL的參數值映射成action方法的參數。在預設情形下,如果你的action方法有個參數的話,MVC框架會檢查進來的請求的資料,看是否有個同樣名稱的對應的HTTP請求值。如果有的話,它會自動將其作為參數傳入你的action方法。

譬如,我們可以利用這個支援來覆寫我們的Detail action方法來,將其簡化,像下面這樣:

除了從請求的查詢字串/表單集合中映射參數值外,ASP.NET MVC框架還允許你使用MVC URL route映射基礎設施在核心URL本身內嵌參數值(譬如,不是使用/Products/Detail?id=3,而是使用/Products/Detail/3 )。

當你建立一個新的MVC專案時,宣告的預設的路徑映射規則擁有這樣的格式,「/[controller]/[action]/[id]」。這意味著,如果URL中在控制器名稱和action名稱之後還有任何子路徑的話,在預設情形下,它將作為一個名為「id」的參數處理,會自動地作為一個方法參數傳給我們的控制器action方法。

這意味著我們現在可以使用我們的Detail 方法來處理從URL路徑(譬如/Products/Detail/3)中獲取ID參數:

我可以對List action使用類似的方法,這樣我們可以將分類名稱作為URL的一部分傳進來(譬如:/Products/List/Beverages)。為了使得程式碼容易閱讀,我對routing規則做了一個小變動,這樣不是把參數名定為「id」,對這個action,它被稱作「category」。

下面是實現了完整URL routing和參數映射支援的ProductsController類別的一個版本:

注意上面List action方法接受一個作為URL一部分的category參數,然後一個作為URL查詢字串一部分的可預設的網頁索引參數(我們將實現伺服端分頁,將使用該參數值來表示我們應該顯示對應分類資料的哪一頁)。

我們的MVC 框架中的可預設的參數是通過Controller Action方法上nullable的型別參數來處理的。因為我們List action的分頁參數是個nullable的int,從語法上來說,即是int? ,如果這個參數存在於URL中,MVC框架會將其值傳給對應方法,如果不存在,會傳入null。參閱我以前的關於?? null coalescing operator文章以瞭解如何操作像這樣傳入的nullable型別參數的一個有用的技巧/訣竅。

建造資料模型物件

至此,我們有了一個 ProductsController類別,內含三個action方法,準備好處理進來的web請求了。下一步將是建造一些類別,來幫我們操作資料庫,從中獲取處理這些請求所需的適當的資料。

在MVC世界裡,「model(模型)」是負責保持狀態的應用元件。在web應用中,這個狀態一般都持久於資料庫之中(譬如,我們也許有一個Product 物件,用來代表我們SQL資料庫裡Products表中的產品資料)。

ASP.NET MVC框架允許你為獲取和管理你的模型,可以使用你想要的任何資料存取模式或框架。如果你要使用ADO.NET DataSets/DataReaders (或者建於它們之上的抽像),你可以那麼做。如果你更喜歡使用像NHibernate, LLBLGen, WilsonORMapper, LINQ to SQL/LINQ to Entities這樣的物件關係映射器(ORM),你也絕對可以那麼做。

對我們的電子商務範例程式,我想用隨 .NET 3.5 和 VS 2008發佈的內建LINQ to SQL ORM 。你可以從我的還在撰寫中的討論LINQ to SQL 的部落格系列中瞭解其中詳情,特別是一定要閱讀一下其中的第一部分第二部分第三部分,和第四部分的文章。

從右擊VS中的MVC web專案的「Models」子目錄開始,選擇「添加新項目」,加一個 LINQ to SQL 模型。在LINQ to SQL ORM 設計器中,我將定義三個資料模型類別,分別映射到SQL Server Northwind資料庫中的Categories, Products, 和Suppliers 表(閱讀我的LINQ to SQL 系列的第二部分學習該怎麼做):

定義完LINQ to SQL 資料模型類別之後,然後我還將添加一個新NorthwindDataContext部分類別 (partial class) 到我們的Models目錄中:

在這個類中,我將定義幾個輔助方法封裝一些LINQ運算式,這些運算式是用來從資料庫中獲取獨特的Category物件,獲取指定分類的所有Product 物件,以及基於指定的ProductID獲取單獨的 Product物件:

這些輔助方法將方便我們在ProductsController類中乾淨利索地獲取所需的資料模型物件 (而不用在Controller類別中編寫LINQ運算式):

至此,我們就有了為完成我們的ProductsController功能所需的所有的資料程式碼和物件。

完成ProductsController類別的實現

基於MVC的應用中的控制器類負責處理到來的請求,處理用戶輸入和交互,並且基於這些輸入和交互執行適當的應用邏輯(獲取和更新儲存在資料庫中的模型資料等等)。

控制器一般對請求生成特定的HTML回應。生成HTML回應的任務是為應用中的「檢視」元件所擁有,這些檢視是通過獨立於控制器的單獨的類別或模板實現的。檢視的目的是完全注重於封裝表現層的邏輯,不應該包含任何應用邏輯或資料庫資料獲取的程式碼的(所有的應用邏輯應當為Controller來處理)。

在一個典型的MVC web流程中,控制器action方法負責處理進來的web請求,使用傳入的參數值執行適當的應用邏輯程式碼,從資料庫中獲取或更新資料模型物件,然後選擇使用一個「檢視」來顯示傳回給瀏覽器的介面回應。作為選擇適當的檢視來顯示的一部分,控制器會明確地以參數的形式向「檢視」傳入檢視所需的所有的資料和變數,以使後者顯示適當的回應:

你也許在想,像這樣分開Controller和View有什麼好處呢?為什麼不把它們放在同一個類別裡呢?像這樣分割應用的主要動機在於幫助你加強應用/資料邏輯與你的介面生成程式碼間的分離。這可以在隔離你的介面顯示邏輯的情形下,極大地方便你單元測試你的應用/資料邏輯。它還有助於使得你的應用更好維護,因為它妨礙了你無意中把應用/資料邏輯加到你的檢視模板裡的可能。

在實現我們ProductsController類別的三個控制器action方法時,我們將根據進來的URL參數值從資料庫中獲取適當的模型物件,然後選擇一個「檢視」元件來顯示適當的HTML回應。我們將使用Controller基礎類別的一個RenderView() 方法來指定我們想要使用的檢視,以及明確地把我們要檢視在顯示回應時使用的特定資料傳入該方法。

下面是我們的ProductsController實現的最終結果:

注意,我們的action方法的程式碼行數目很小(每個方法只有2行),部分原因是因為URL參數分析邏輯完全是由MVC框架為我們做的(給我們省了很多行程式碼),還有部分原因是因為產品瀏覽場景從業務邏輯的角度來說相當簡單(涉及的action方法都是唯讀的顯示場景)。

但總的來說,你經常會發現你有的都是些有時被稱為「瘦控制器」的東西,即控制器方法充滿了相當簡短的action方法(少於10行程式碼)。這經常是好的跡象,表明你非常乾淨地封裝了你的資料邏輯,也非常好地分隔了你的控制器邏輯。

單元測試ProductsController

你也許會感到驚奇,我們要做的下一步居然是測試我們的應用邏輯和功能。你也許會問,這怎麼可能呢?我們還沒有實現我們的檢視呢,我們的應用目前並不顯示一個HTML tag。其實呢,使得MVC方法有魅力的部分原因就是我們可以完全獨立於檢視/Html生成邏輯來測試Controller和Model 邏輯。在下面你將看到,我們甚至可以在建立檢視前單元測試這些物件。

為單元測試我們在編寫的ProductsController類別,我們將往測試專案裡加一個ProductsControllerTest類別,這個測試專案是在我們使用Visual Studio建立我們的ASP.NET MVC應用時,預設添加到我們的解決方案裡的:

然後我們將定義一個簡單的單元測試,測試我們的ProductsController的 Detail action方法:

ASP.NET MVC 框架是特地設計來促成輕鬆的單元測試的。框架中的所有的核心API和契約都是介面,提供了大量的擴展點以促成輕鬆的物件注入和客制(包括使用象Windsor, StructureMap, Spring.NET, 和ObjectBuilder這樣的IoC容器的能力)。開發人員將能夠使用內建的mock類,或者使用任何.NET 型別mock框架來模擬他們自己的MVC相關物件的測試版本。

在上面的單元測試中,你可以看到一個例子,我們是如何在叫用 Detail() action 方法之前,往我們的ProductsController裡注入了一個偽(dummy)「ViewFactory」實現的。這麼做的話,我們就覆蓋了預設的ViewFactory,否則的話,預設的ViewFactory會建立和顯示我們的檢視。我們可以使用這個測試ViewFactory實現來做隔離,只對我們ProductController的Detail action的行為進行測試(而不必叫用實際的檢視來做測試)。注意我們是如何在Detail() action方法被叫用之後,使用了三個 Assert 語句來核實該方法的正確的行為確實發生了(具體地說,該方法獲取了正確的Product物件,然後將它傳給了適當的檢視)。

因為我們可以mock和模擬MVC框架中的任何物件(包括 IHttpRequest 和 IHttpResponse 物件),你不用再在web服務的環境裡運行單元測試,我們可以在常規的類別庫裡建立我們的ProductsController物件,然後對它直接測試。這可以極大地加快單元測試的運行速度,以及簡化對它們的配置和運行。

如果我們使用 Visual Studio 2008 IDE,我們還可以輕易地跟蹤我們運行測試的結果(這個功能現在已經成為VS 2008 專業版的一部分):

我想你會發現ASP.NET MVC 框架極大地方便了測試的編寫,而且促成了非常好的TDD流程。

使用檢視顯示介面

我們完成了我們電子商務應用的產品瀏覽部分的應用+資料邏輯的實現和測試,現在我們需要實現相關的HTML介面。

我們將通過實現「檢視」來實現,這些檢視將使用我們ProductsController的action方法在叫用RenderView() 方法時提供的跟檢視有關的資料物件,來顯示適當的介面:

在上面的程式碼例子裡,RenderView方法的「Categories」參數表示我們要顯示的檢視名稱,第二個參數是我們要傳給檢視物件並要檢視物件據此顯示適當HTML介面的分類物件的列表。

ASP.NET MVC框架支援使用任何模板引擎(包括象NVelocity, Brail,以及你自己想要編寫的任何模板引擎)來幫助生成介面。在預設情形下, ASP.NET MVC 框架使用ASP.NET中現有的ASP.NET 頁面 (.aspx), 母版頁 (.master), 和用戶控制項 (.ascx) 。

我們將使用內建的ASP.NET 檢視引擎來實現我們的電子商務應用的介面。

定義Site.Master檔案

因為我們將要在網站上建造很多頁面,我們先來定義一個母版頁,用以封裝整個網站公用的HTML佈局/樣式。我們將在我們專案的\Views\Shared 目錄裡建立一個名為「Site.Master」的檔案:

我們可以引用一個外部的CSS樣式檔案來封裝整個網站的所有樣式,然後使用母版頁來定義網站總的佈局,以及指定我們要具體頁面填充相關內容的內容placeholder 區域。在做的時候,我們也可以使用VS 2008 中的新設計器的所有的酷功能,包括HTML分割檢視設計器編著CSS嵌套母版頁支援等。

理解/Views目錄結構

在預設情形下,當你使用Visual Studio建立新的ASP.NET MVC 專案時,它會在「Views」根目錄下生成一個「Shared」子目錄。這是存放應用中為多個控制器所共享的母版頁,用戶控制項和檢視的推薦使用的地點。

在建造為特定個別控制器所用的檢視時,預設的 ASP.NET MVC 約定是,把它們存放在\Views 根目錄的子目錄裡。在預設情形下,子目錄的名字應該對應於控制器的名字。譬如,因為我們正編寫的Controller類別叫「ProductsController」,在預設情形下,我們將在\Views\Products 子目錄裡存放跟它相關的特定檢視:

當我們在一個特定的Controller中叫用 RenderView(string viewName)方法時,MVC框架會自動地首先在\Views\ControllerName 目錄裡尋找對應的.aspx 或 .ascx檢視模板,如果它找不到適當的檢視模板,然後它會在 \Views\Shared目錄尋找。

建立一個Categories檢視

我們可以在 Visual Studio 中 Products 目錄上使用「添加新項」選單選項,然後選擇「MVC檢視網頁」項模板,為我們的ProductsController 建立一個「Categories」檢視。這會生成一個新的.aspx 頁面,我們可以將它跟我們的 Site.Master母版頁相關聯,來得到總的外觀(就像母版頁一樣,你會得到即見即所得設計器的支援):

在使用MVC模式建造應用時,你要把你的檢視程式碼盡可能地保持簡潔,確認檢視程式碼純粹是用來顯示介面。應用和資料獲取邏輯應該只在Controller類裡編寫。然後Controller類就可以在叫用RenderView 方法時選擇把所需的資料物件傳遞給檢視。譬如,在下面,我們的ProductsController類的 Categories action方法中,我們把 一個Category物件的List集合傳給了Categories檢視:

MVC檢視頁預設是從System.Web.Mvc.ViewPage 基礎類別繼承而來的,該基礎類別提供了可為我們構建介面時所用的許多特定於MVC的輔助方法和屬性。ViewPage的其中一個屬性名叫「ViewData」,通過它,你可以存取Controller作為參數傳給 RenderView()方法的特定於檢視的資料物件。

從你的檢視裡,你可以後期繫結或以強型別的方式存取「ViewData」。如果你的檢視是從ViewPage繼承而來,那麼ViewData屬性是個後期繫結的字典。如果你的檢視是從基於泛型的ViewPage<T>繼承而來,其中T表示Controller傳給檢視的ViewData的資料物件的型別,那麼ViewData屬性就是強型別的,匹配你的Controller傳入的資料的型別。

譬如,如下所示的我的Categories檢視的後台類是從ViewPage<T>繼承而來,我指明T為Category物件的一個List

這意味著在我的檢視程式碼裡操作ProductsController.Categories() 提供的List<Category> ViewData時,我將得到完整的型別安全, intellisense和編譯時檢查:

顯示Categories檢視:

如果你還記得本文章最前面的截圖的話,我們要在我們的 Categories 檢視裡顯示產品分類列表:

我可以在我的Categories檢視實現裡用2種方式編寫這個HTML介面生成程式碼:1) 在.aspx 檔案裡使用行內程式碼, 或者 2) 在.aspx 檔案中使用伺服器控制項,然後在後台程式碼裡使用資料繫結。

顯示方法1:使用行內程式碼

目前的ASP.NET網頁, 用戶控制項和母版頁支援使用 <% %> 和 <%= %>的句法來在html 標識內嵌入顯示程式碼。我們可以在Categories 檢視裡使用這個技巧,輕鬆地編寫一個foreach迴圈,來生成HTML分類列表:

VS 2008在源碼編輯器內為VB和C#提供完整的程式碼intellisense。這意味著,在對傳入檢視的Category模型物件操作時,我們將得到intellisense:

VS 2008還為行內程式碼提供了完整的偵錯器支援(允許我們在偵錯器對檢視中的程式碼設置中斷點以及動態檢查任何東西):

顯示方法2:使用伺服端控制項

 

ASP.NET網頁,用戶控制項和母版頁還提供對使用宣告式伺服端控制項封裝HTML介面生成的支援。不是象上面那樣使用行內程式碼,我們可以使用 .NET 3.5中新的<asp:listview> 控制項來生成列表介面:

注意上面 ListView 控制項封裝了顯示值列表的情形,還負責處理列表中沒有任何東西的情形(<EmptyDataTemplate>省略了需要在標識中編寫 if/else 語句的麻煩)。然後我們可以像下面這樣,在後台程式碼裡,將我們的分類物件繫結到listview控制項上:

重要注意事項:在MVC世界裡,我們只想要把顯示程式碼放在我們檢視的後台程式碼裡,不包括任何應用或資料邏輯。注意上面我們只有把強型別的Category物件的ViewData集合指派給ListView控制項的邏輯。我們的ProductsController控制器類才是實際從資料庫獲取Category物件列表的責任者,不是檢視。

我們檢視模板的ListView伺服端控制項版本然後就會生成跟上面行內程式碼版本完全一樣的HTML。因為我們在頁面裡沒有 <form runat=」server」>控制項,ViewState,ID值以及其他的標識都不會生成,只有純粹的CSS友好的HTML:

Html.ActionLink方法

你也許注意到的一件事情是,在上面檢視程式碼片斷中行內程式碼以及伺服端控制項兩個版本中對一個Html.ActionLink 方法的叫用:

Html物件是 ViewPage 基礎類別的一個輔助屬性,ActionLink方法是它的一個輔助方法,它方便你動態地生成連回到控制器的action 方法的HTML超連結。如果你看一下上面生成的HTML輸出圖,你可以看到一些由該方法生成的一些HTML輸出例子:

<a href=」http://weblogs.asp.net/Products/List/Beverages」>Beverages</a>

我使用的Html.ActionLink方法的簽名是這樣的:

string ActionLink(string text, object values);

第一個參數表示要顯示的超連結的內容(譬如<a>這裡是文字</a>),第二個參數是個匿名物件 ,它代表用以生成實際URL的一串值,你可以認為它是生成字典的一個比較乾淨的方式。我會在將來討論URL routing引擎的部落格文章裡仔細討論這個參數的運用情形。但簡而言之,你可以使用URL routing系統既處理進來的URL,也可以用它來生成你可以在傳回的HTML輸出的URL。如果我們的routing規則是像這樣的:

/<controller>/<action>/<category>

那麼在ProductController的Category檢視裡編寫這樣的程式碼時:

<%= Html.ActionLink(「Click Me to See Beverages」, new { action=」List」, category=」Beverages」 } %>

ActionLink方法就會使用你應用的URL映射規則,換進你的參數,生成這樣的輸出:

<a href=」http://weblogs.asp.net/Products/List/Beverages」>Click Me to See Beverages</a>

這方便了在你應用中生成URL和到你的控制器的AJAX回呼。它也意味著你可以在一個地方更新你的URL routing規則,你整個應用中的程式碼會在對進來的URL的處理和外出的URL的生成過程中自動採用新的變化。

重要注意事項: 為加強可測試性,目前的MVC框架並不支援你檢視中針對伺服端控制項的postback事件,取而代之的是,ASP.NET MVC應用生成超連結和對控制器action的AJAX回呼,然後只使用檢視(以及其中的任何伺服端控制項)顯示輸出。這有助於確保你的檢視邏輯保持在最小限度,只注重於顯示,以及你可以單元測試你的Controller類,獨立於你的檢視,核實所有的應用和資料邏輯行為。我在將來的文章裡會對此做更深入的討論。

結語

這第一個文章非常長,但希望它對新的ASP.NET MVC框架中所有不同的元件是如何組合在一起的,如何使用它打造常見的現實世界場景的應用提供了一個相當廣泛的綜覽。 ASP.NET MVC的第一個公開預覽版將在幾個星期內發佈,你將能夠使用它來做我上面描述的一切。

雖然很多MVC固有的概念(特別是關注分離的觀念)對該文章的很多讀者來說是比較新的,但希望本文章展示了我們正在開發的ASP.NET MVC實現是如何很乾淨地嵌合到現有的ASP.NET, .NET, 和 Visual Studio框架中的。你可以使用.ASPX, .ASCX 和 .MASTER檔案以及ASP.NET AJAX建立你的ASP.NET MVC 檢視。今天ASP.NET中的非介面功能,譬如表單認證, Windows認證, 成員,角色, Url授權, 快取, Session 狀態, 用戶信息,健康監測, 配置,編譯,本地化以及 HttpModules/HttpHandlers 都是完全支援MVC模型的。

如果你不喜歡MVC模型,或者發現它對你的開發風格來說並不自然的話,你完全不必強用它的。它完全只是提供選項,並不替代現有的 WebForms Page Controller 模型。WebForms和MVC這2個模型在以後都會得到完全支援和改進。如果你想的話,你甚至可以建造一個應用,部分使用WebForms編寫,部分使用MVC編寫。

如果你喜歡上面文章裡的東西的話(或者感興趣想進一步瞭解的話),留意一下我這段時間的部落格。我將進一步討論MVC概念,使用它們來進一步建造我們的電子商務應用,展示更多的MVC特性。

希望本文對你有所幫助,

Scott