ASP.NET MVC是一款基於ASP.NET的MVC模式的實現框架。通過使用ASP.NET MVC框架,開發人員能夠非常方便地完成應用程序前台頁面的開發工作。優秀的前台展示,對於大型企業級應用而言,是非常重要的組成部分,而ASP.NET MVC則為實現這一重要組成部分提供了技術和平台支持。目前,ASP.NET MVC已經到了4.0 Beta的版本,但我仍然打算以ASP.NET MVC 3為基礎,通過幾篇文章的篇幅,介紹一些ASP.NET MVC的實用技術,比如:如何實現自定義的認證機制、如何實現多主題效果的支持等。這些內容在網上也或多或少地提供了一些解決方案,但有些也不算太完整,有些又寫的很含糊。因此,這幾篇連載的文章會對每個實用技術進行完整的介紹,並在每篇文章結尾部分給出案例源代碼及其使用方式,以供讀者朋友們參考。
首先說明一下,這幾篇文章不會對ASP.NET MVC中的概念進行描述,也不會太多地去深究某些技術實現細節,這些文章應該可以看成是幾篇連載的“菜譜(Cook Book)”,讀者朋友通過閱讀並實踐文中所述的內容,就能夠獲得所需的解決方案。雖然不能從根本上“解惑”,但也能在實踐上為讀者指明方向。此外,本人並不擅長.NET的前台技術,所以這幾篇文章中提供的解決方案也不一定是最好的,如果讀者朋友們有更好的想法或者思路,歡迎大家踴躍留言。
在介紹這些實用技術以前,首先讓我們了解一下在企業級應用程序架構中,ASP.NET MVC的應用模式,這對我們今后介紹各種技術有一定的幫助,也算是能夠讓我們在架構的這個問題上達成一致。之后,我們再看看標准的ASP.NET MVC應用程序的數據庫問題。
ASP.NET MVC的應用模式
在討論ASP.NET MVC的應用模式時,對於MVC中的View和Controller的理解,我想應該不會存在什么太大的異議:View主要負責管理頁面的顯示布局和效果,並將Model中的數據展現在頁面上;Controller則負責Model數據的獲取與組織,並將數據以某種形式綁定到View上,或者從View上獲取數據並對其進行必要的操作。現在需要討論的是MVC中的這個“M”,從概念上講,它應該是指View Model,因為它是View的數據源,但事實上在不同的應用程序中,它可能會被賦予不同的含義,於是也就產生了三種常見的ASP.NET MVC應用模式。比如,在實際項目中,我們有可能直接將Domain Model用作View Model,以便Controller能夠直接操作Domain Model以完成業務邏輯處理;我們有可能把用於與分布式服務進行通信的數據傳輸對象(Data Transfer Objects)用作View Model,以便Controller能夠通過分布式服務來獲取或者提交數據,進而在分布式服務中對這些數據進行處理;我們還有可能在數據傳輸對象(Data Transfer Objects)與View Model之間再做一層映射,以解耦View Model與數據傳輸對象,因為在某些情況下View Model與數據傳輸對象仍然存在“阻抗失衡。針對這三種不同的應用模式,應用程序也會有着架構設計和部署上的差異。下面我們對這三種不同的應用模式進行詳細討論。
Domain Model as View Model
對於中小型應用程序而言,這種應用模式比較常見,在這種模式中,Domain Model被用作View Model,直接成為View的數據源,被顯示在頁面上。這有點像“數據源架構模式(Data Source Architectural Patterns)”中的Active Record模式。在Active Record模式中,對象用來表示數據庫中的一張表或者一個視圖,同時它又包含一些業務邏輯,於是,它混合了領域對象和數據訪問對象的職責。在ASP.NET MVC中,如果采用Domain Model as View Model的模式,那么情況也就非常相似,因為這里的Model同時包含了領域模型和視圖模型的職責。當然,除非是特殊情況,比如應用程序本身規模比較小,否則Domain Model as View Model並非一個合理的應用模式,它違背了軟件設計的SRP(Single Responsibility Principle)的原則。
Domain Model as View Model的實現比較簡單,只需要將Domain Model寫在ASP.NET MVC Web應用程序中就可以了。為了能夠參與View Model的職責,我們通常會在Domain Model上加入一些驗證特性,比如使用RequiredAttribute來標注當前的屬性是一個必填字段,以便基於javascript的客戶端驗證機制能夠在必要的時候將錯誤信息提示給用戶。在數據存儲部分,通常可以選用ORM,將Domain Model直接持久化到數據庫;也可以使用數據訪問對象(Data Access Objects),配合使用ADO.NET來實現數據訪問。常見的數據訪問模式可以參考:Table Data Gateway、Row Data Gateway、Active Record以及Data Mapper。
從物理部署的角度看,首先需要一台數據庫服務器作為數據持久化基礎架構,然后是一台IIS Web服務器,用來運行ASP.NET MVC Web應用程序,客戶端瀏覽器通過訪問這個IIS站點來完成系統交互。這是一個典型的三層結構,我用下圖簡要地描述了Domain Model as View Model的應用模式(說明:雖然將物理機器的部署和應用程序的邏輯結構畫在一起並不是那么自然,畢竟邏輯視圖和部署視圖是兩個完全不同的表述,但為了讓讀者能夠看到在不同的物理部署節點上,運行着哪些邏輯組件,我還是勉強將它們畫在了一張圖中,因此如果在該圖中如果有什么不合理的地方,還請讀者指正):
Data Transfer Object as View Model
這種應用模式往往可以跟面向領域的架構模式相結合,提供比較合理的企業級應用程序解決方案。在這種應用模式中,領域模型不充當View Model的角色,它甚至與View Model毫無關系,只是作為一個業務組件運行在另一台物理服務器上。根據領域驅動設計(DDD)的實踐指導,應用層通過倉儲來管理領域對象的生命周期、使用這些領域對象和領域服務來完成所需的業務邏輯、並在領域對象與數據傳輸對象之間進行映射操作。當我們使用WCF作為服務供應者(Service Provider)時,通常會將WCF的Data Contract作為數據傳輸對象(Data Transfer Object),因此,應用層會完成Data Contract和領域對象之間的轉換,然后通過WCF服務向外部提供服務接口。
作為向最終用戶提供界面服務的ASP.NET MVC Web應用程序,它會通過服務引用(Service Reference)來使用由WCF公布的服務。在這個過程中,.NET會在ASP.NET MVC Web應用程序中創建一系列的WCF客戶端代理類型,其中包括Client Channel、Data Contracts等。於是,MVC中的Controller會調用WCF服務以獲得Data Contract對象,之后將這些Data Contract對象作為View的數據源綁定到View上。因此,整個過程事實上是使用數據傳輸對象作為View Model。類似地,我也針對這種應用模式畫出了整個應用程序的結構細節,讀者可以與上圖進行比較:
在這種應用模式中,ASP.NET MVC不再參與任何業務邏輯,它僅僅是為用戶界面提供服務,這樣就完全解耦了用戶界面層與業務層之間的關聯關系。也就是說,我們可以不選用ASP.NET MVC技術,而選擇其它的展現層技術(比如WPF或者Windows Forms)來設計和開發應用程序的展現部分。而在業務邏輯部分,基本上可以以DDD為指導進行設計與開發,這樣做不僅可以將大部分分析與設計的精力放在領域模型上,而且還能夠很自然地(或者說更合理地)應用一些企業架構模式。
這里其實會有一個技術問題,就是在ASP.NET MVC部分,Data Contract代理類型都是在Service Reference的過程中自動產生的,如果將這些Data Contract,也就是數據傳輸對象,直接作為View Model綁定到View上,那么似乎那些用於客戶端驗證的Attributes就無法添加到這個View Model上。事實上我覺得MS應該已經意識到了這種應用的可行性,所以已經有現成的解決方案了。這個解決方案得益於兩件事情:C#的partial class,以及MetadataTypeAttribute。
比如,在自動生成的代理類中,有如下這個Data Contract:
[DataContract]
public partial class Customer {
[DataMember]
public string Name { get; set; }
}
那么我們可以使用下面的方法為Customer的Name屬性添加驗證規則:首先,針對Customer定義一個Metadata類,使其包含與Customer相同名稱的屬性(當然,只需要包含那些你需要添加驗證特性的屬性即可),然后在這個Metadata類的屬性上,添加驗證特性,如下:
public class CustomerMetadata {
[Required(ErrorMessage="Name must be specified.")]
public string Name { get; set; }
}
之后,使用partial關鍵字重定義Customer類,並在這個partial類上使用MetadataTypeAttribute,將CustomerMetadata“綁定”到Customer類即可:
[System.ComponentModel.DataAnnotations.MetadataTypeAttribute(typeof(CustomerMetadata))]
partial class Customer { }
Data Transfer Object as View Model的應用模式同時也向我們展示了.NET技術在領域驅動設計實踐中的應用。接下來的幾篇文章,都會以這種應用模式為背景,對ASP.NET MVC的一些實用技術進行介紹。
Mapped DTO as View Model
Mapped DTO,也就是根據View的需求,通過映射技術,將數據傳輸對象組裝成View所需的View Model。其實與Data Transfer Object as View Model相比,它只是在View Model和Data Transfer Object之間又增加了一層映射處理,應用程序的其它部分與DTO as View Model完全相同。這種映射處理可以使用一些輔助的第三方框架(比如AutoMapper等)完成。在這里,我也就不打算對這種應用模式進行詳細介紹了。
ASP.NET MVC應用模式大致也就是上述幾種,這些討論都是以MVC中的“M”為中心展開的。為了能夠開始接下來的實用技術介紹,在此還需要簡單地討論一下ASP.NET MVC應用程序的數據庫部分。
ASP.NET MVC應用程序的數據庫
每當我們新建一個ASP.NET MVC應用程序的時候,Visual Studio模板都會幫我們把整個項目都建好,並會默認地使用SQL Server Express作為這個應用程序的后台數據庫。如果你的機器上裝有SQL Server Express,那么在你第一次運行ASP.NET MVC應用程序后,你應該就會在App_Data下找到一個aspnetdb.mdf的數據庫文件,所有與ASP.NET MVC應用相關的數據表都會保存在這個數據庫文件中。
當然,在大多數情況下,你有可能不會將SQL Server Express用作上線系統的數據庫,或許你更傾向於使用SQL Server 2008 Enterprise Edition作為你的后台數據庫,那么你就需要定制ASP.NET MVC應用程序。首先,打開Visual Studio 2010 Command Prompt,在命令行輸入aspnet_regsql然后回車,這將打開ASP.NET SQL Server Setup Wizard:
在這個界面上單擊Next按鈕,在Select a Setup Option選項卡中,選擇Configure SQL Server for application services選項,然后單擊Next:
在Select Server and Database選項卡中,選擇你要使用的數據庫名稱,然后單擊Next:
在Confirm Your Settings選項卡中直接單擊Next按鈕,這樣,向導就會將所需的數據庫對象同步到你所選擇的數據庫中,然后在完成頁面單擊Finish按鈕:
回到SQL Server管理控制台,展開我們剛剛創建的數據庫,我們可以看到,所有所需的數據庫對象都已經創建好了:
現在,回到ASP.NET MVC應用程序,在web.config中把數據庫連接字符串直接改掉就可以了。
如果你的應用程序采用了上述“Data Transfer Object as View Model”的模式,那么你的后台數據庫又將是另外一個景象:它或許連這些ASP.NET MVC所需的數據庫對象都沒有,於是你只能夠自定義Membership Provider,然后在ASP.NET MVC中使用這個自定義的Membership Provider。在Tiny Library CQRS案例中已經采用了自定義的Membership Provider,有興趣的讀者可以先去了解一下,不過這部分內容也會在后續的篇章中詳細介紹。
總結
本文為《ASP.NET MVC實用技術》系列文章的開篇,首先對ASP.NET MVC的應用模式進行了簡單介紹,然后瀏覽了一下與ASP.NET MVC程序數據庫相關的一些內容。在接下來的文章中,我會以ASP.NET MVC 3為基礎,詳細介紹一些實用技術,並會在每篇文章末尾給出完整源代碼案例,供讀者參考使用。
擴展閱讀
有興趣的讀者可以參考以下資料,以對ASP.NET MVC的應用模式作進一步了解:
- View Model pattern and AutoMapper in ASP.NET MVC Applications
- ASP.NET MVC View Model Patterns
- View Models in ASP.NET MVC
- How we do MVC – View models
- Asp.Net MVC ViewModel with AutoMapper







