??? 在開發應用程序的過程中,經常需要處理許多大大小小的數據,例如,SQL Server數據庫存取、連接AD(Active Directory)數據庫進行驗證、調用外部Web Service取得數據等。除了訪問數據外,也經常需要對數據做成格式驗證、邏輯驗證等等。
?一、Model的責任
??? 在ASP.NET MVC中,Model負責所有與“數據”有關的任務。所以,不管是Controller或是View,都會參考到Model里定義的所有數據類型,或是用到Model里定義的一些數據操作方法,例如,新增、刪除、更改、查詢等。
??? 在Model里的程序,由于“只能”跟數據域商業邏輯有關,所以就不負責處理所有與數據處理無關的事,或是用來控制網站的運行流程等,而是專注于如何有效率地提供數據訪問機制、交易環境、數據格式驗證、商業邏輯驗證等工作。
??? 由于Model的獨立性非常高,如果你在一個Visual Studio方案中,有多個要開發的項目,比如有時我們會將Model獨立成一個項目,讓Model項目共享于不同的項目之間。
二、開發Model的基本觀念
??? 當采用ASP.NET MVC框架時,雖然在Model層的開發技術繁多,而且ASP.NET MVC也保留了許多彈性,讓各個不同的數據訪問技術都能跟ASP.NET MVC集成。不過,若要充分發揮ASP.NET MVC快速開發的優勢,還是建議讀者在Model層采用ORM信息訪問技術來開發,例如,LINQ to SQL、Entity Framework、NHibernate、Telerik OpenAccess ORM等。
1.何謂ORM
??? ORM的全名是Object Relational Mapping,中文翻譯為“對象關系映射”,是一種編程技術,用于實現面向對象程序語言里,不同類型系統之間的數據轉換。通常在實務的應用上,大多數情況都會應用在數據庫與面向對象程序自檢的類型轉換,例如,SQL Server中的關系型數據與.NET類型對象之間的轉換等。
??? 換句話說,ORM是將結構化的關系型數據,映射成面向對象模型。如果以Entity Framework來說,就是試圖將關系數據庫的各種數據轉換成.NET原生對象,或是將.NET模型類對象數據轉換成關系型數據。
??? 使用ORM開發技術跟常規使用ADO.NET開發技術最大的差異,就在于操作“數據”的方便性與彈性。以往在使用ADO.NET開發數據訪問程序時,開發人員通常必須先了解完整的數據庫操作方法,才能順利地從數據庫取得數據,或是將對象的數據保存到數據庫中。例如,要編寫操作SQL Server數據庫中的數據,就必須先學習T-SQL的使用方式;若要操作Oracle數據庫中的數據,也必須先學習Oracle的SQL查詢語法。學會之后還要學習各式ADO.NET的標準API,才能知道如何正確地與數據庫交互,明白是要進行查詢數據、更新數據、新增數據還是刪除數據等。不同的數據庫,在設計邏輯與SQL語法上都會有些小差異,而導致開發數據訪問的程序代碼缺乏效率。如此一來,也有違“關注點分離”的特性,若是套用“關注點分離”特性,照理說在開發.NET應用程序時,應該專注在對象的操作上,而非數據庫數據的處理,當采用ORM開發技術后,便可以幫助我們達到這個目的。
??? Entity Framwork是建構在ADO.NET數據提供者模型之上,也就是說,只要.NET運行環境能夠使用ADO.NET連接數據庫,Enity Framework便能順利支持,在.NET運行環境下默認已經支持SQL Server 2005以上版本,如果要支持其他如Oracle、MySQL、SQLite、PostgreSQL等各式各樣的關系數據庫基本上也是沒問題的,只要操作出相對應的Entity Framework數據提供者即可。
2.數據庫開發模式
??? 采用Entity Framework或其他ORM開發極速,有時還可區分三種不同的開發模式,分別是數據庫優先開發模式(Database First Development)、模型優先開發模式(Model First Development)、程序代碼優先開發模式(Code First Development)。
(1)數據庫優先開發模式
??? 顧名思義,數據庫優先開發模式就是針對數據庫進行設計,以數據庫里定義的數據結構(Schema)為主。當應用程序在卡法的時候,必須依據數據庫的結構設計來進行開發,使用的ORM框架必須能夠依據數據庫結構設計生成相應的對象模型,才能提供給應用程序來使用。
(2)模型優先開發模式
??? 模型優先開發模式是指在ORM架構中創建對象模型,讓應用程序能夠依據這些對象模型進行開發。需要實際訪問數據庫時,只要通過Entity Framework數據提供者的協助,動態生成相應的SQL語法,即可創建出完整的數據庫。
??? 一般來說,支持模型優先開發模式的ORM框架,都會有相對應的程序代碼生成技術,在模型被創建的同時自動生成相對應的程序代碼。以Entity Framework為例,在搭配Visual Studio的幫助下,即可通過Entity Framework設計工具,幫你創建對象模型,只要保存后,就會自動生成相關程序代碼。
(3)程序代碼優先開發模式
??? 程序代碼優先開發模式是一種非常新穎的開發模式,也就是讓開發人員直接依據需求,編寫類別與屬性(程序代碼),而這些撰寫的類別與屬性正是定義出應用程序所需的數據模型,并且通過ORM框架的管理,便可讓這些POCO類型,轉換成實體模型(Entity Model)。
??? 直到程序開始運行后,通過ORM框架,就可以自動依據這些類別,定義創建數據庫、表格、字段與其他數據結構(Schema)。這樣開發人員便可以完全不需要接觸數據庫這一端的各種管理工作(如創建表格字段、設計數據表、設計數據表關聯等),也不用學習各式數據庫的使用差異(如SQL Server、Oracle、MySQL、SQLite等),省去這些工作之后,開發人員更能專注在應用程序的需求開發,而不會因為不熟悉數據庫操作而束手束腳。
三、LocalDB介紹
??? 微軟最新推出的SQL Server 2012 Express LocalDB,是一種SQL Server Express的運行模式,特別適合在開發環境使用,也內建在Visual Studio 2012之中。
1.LocalDB的運作方式
??? 在安裝好SQL Server 2012 Express LocalDB之后,默認會有個實例名,為v11.0。
2.如何連接LocalDB實例
(1)使用Management Studio連接LocalDB
??? 在連接對話框中輸入正確服務器名稱,(localdb)\v11.0。只有新版的SQL Server 2012 Management Studio才能識別這組新的服務器名稱。
(2)通過.NET程序連接LocalDB實例
??? 連接字符串如下。
Server=(LocalDB)\v11.0; Integrated Security=true; AttachDbFileName=D:\Data\MyDB1.mdf
3.管理LocalDB自動實例
4.管理LocalDB具名實例
四、使用Code First創建數據模型
??? ASP.NET MVC4與當前最新版的Entity Framework 5同時上市,尤其強化了程序代碼優先開發模式的支持。
1.創建數據模型
(1)聲明主鍵
??? 要想在Entity Framework聲明主鍵,最簡單的方式就是不要聲明,直接把屬性名稱設置為Id或是類名+“Id”也可以,并將該屬性指派為int類型即可。EF Code First會自動識別出這個字段就是表格里的主鍵,并且會加上自動編號的識別規格設置。
???? 當希望使用其他域名當作主鍵時,就可能遇到一些麻煩。可以加上key屬性(Attriute),引用System.ComponentModel.DataAnnotations命名空間。
using System; using System.ComponentModel.DataAnnotations;namespace MvcGuestbook.Models {public class Guestbook{[Key]public int No { get; set; }public string Name { get; set; }} }
(2)聲明必填字段
??? 聲明為string的屬性,在數據庫表格里的字段設置為NOT NULL。如果需要改為必填字段,可以加上Required屬性,引用System.ComponentModel.DataAnnotations命名空間。
using System; using System.ComponentModel.DataAnnotations;namespace MvcGuestbook.Models {public class Guestbook{[Key]public int No { get; set; }[Required]public string Name { get; set; }} }
(3)聲明允許NULL字段
??? 聲明為DateTime的屬性,在數據庫表格里的字段設置為NOT NULL。如果需要改變允許為空字段,可以加上一個問號,不需要引用任何命名空間。
using System; using System.ComponentModel.DataAnnotations;namespace MvcGuestbook.Models {public class Guestbook{[Key]public int No { get; set; }[Required]public string Name { get; set; }public string Emai { get; set; }[Required]public string Message { get; set; }public DateTime? CreatedOn { get; set; }} }
(4)聲明字段長度
??? 我們也經常會在數據庫中限定特定字段的字符串長度,以方便日后創建字段索引,可以使用MaxLength屬性,引用System.ComponentModel.DataAnnotations。
using System; using System.ComponentModel.DataAnnotations;namespace MvcGuestbook.Models {public class Guestbook{[Key]public int No { get; set; }[Required][MaxLength(5)]public string Name { get; set; }[MaxLength(200)]public string Emai { get; set; }[Required]public string Message { get; set; }public DateTime? CreatedOn { get; set; }} }
??? 也可以設置StringLength屬性來限定字段長度。
Model類中為屬性添加MaxLength說明,需要引用(?? ?)命名空間。
A.System.Linq???????????????????????????? B. System.ComponentModel
C. System.ComponentModel.DataAnnotations? D. System.Collection.Generic
(5)聲明字段默認值
(6)聲明特定屬性不是數據庫中的字段
??? 只要數據模型中出現公開屬性,默認就會在數據庫中創建一個對應的字段,但如果在數據模型中的屬性,是一個動態計算的屬性,我們并不想在數據庫中新增對應的字段時,該怎么辦?可以加上NotMapped屬性(Attribute),引用System.ComponentModel.DataAnotations.Schema。
using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema;namespace MvcGuestbook.Models {public class Guestbook{[Key]public int No { get; set; }[Required][MaxLength(5)]public string Name { get; set; }[MaxLength(200)]public string Emai { get; set; }[Required]public string Message { get; set; }[DatabaseGenerated(DatabaseGeneratedOption.Computed)]public DateTime? CreatedOn { get; set; }[NotMapped]public string FamilyName{get{return this.Name.Substring(0, 1);}set{this.Name = value.Substring(0, 1) + this.Name.Substring(1);}}} }
?
使用EF Code First在模型中聲明特定屬性不是數據庫中的字段,應使用(? )關鍵字。
A.Key??? B.Required??? C.DisplayName??? D.NotMapped
(7)DisplayFormat
獲取數據字段的顯示格式。例如可用于設置日期的格式。
(8)DataType
獲取與數據字段關聯的類型。例如DataType.Date。
(9)DatabaseGeneratedOption
DatabaeGeneratedOption.Computed,計算列
DatabaseGeneratedOption.Identity,標識自動加1
DatabaseGeneratedOption.None,沒有標識,不自動加1
(10)Column
將類與數據庫中的表相關。?[Column(“FirstName”)]public string FirstMidName{get;set;}
2.創建數據上下文類
????數據上下文類,在Entity Framework Code First開發模式下非常重要,主要用來追蹤與識別對象的變更追蹤。少了這個類別,Entity Framework就完全無法運作。
??? 自動添加數據上下文類,可以在添加控制器時操作。
public class MvcGuestbookContext : DbContext{public MvcGuestbookContext(): base("name=DefaultConnection"){}public DbSet<Guestbook> Guestbooks { get; set; }}
??? 構造函數中的DefaultConnection為連接字符串的名字,這個連接字符串必須存儲在web.config中。DbSet<Guest>類型的Guestbooks變量,代表的是Guestbook這個類型的數據庫集合對象。
??? 如果希望將Guestbook數據模型被聲明成只讀,不讓應用程序對其寫入任何數據,那么可以修改數據上下文類,讓DbSet集合屬性只提供get實體。
public class MvcGuestbookContext : DbContext{public MvcGuestbookContext(): base("name=DefaultConnection"){}public DbSet<Guestbook> Guestbooks{get { return Set<Guestbook>(); }}}
?
3.設計模型之間的關聯性
??? 在設計數據庫結構時,當遇到表格與表格間有關聯存在時,一般會通過創建外鍵(Foreign Key)的方式設計表格之間的關聯關系。
(1)設計模型之間的一對多關聯
public class Guestbook{[Key]public int No { get; set; }[Required][MaxLength(5)]public string Name { get; set; }[MaxLength(200)]public string Emai { get; set; }[Required]public string Message { get; set; }public virtual Member Member { get; set; }}
public class Member{public int Id { get; set; }[Required][MaxLength(5)]public string Username { get; set; }[MaxLength(10)]public string Password { get; set; }public DateTime? LastLoginTime { get; set; }
public virtual ICollection<Guestbook> Guestbooks { get; set; }}
?(2)設計模型之間的多對多關聯
??? 將上述代碼中的public virtual Member Member { get; set;},更改為:
public virtual ICollection<Member> Members { get; set; }
4.啟用延遲裝入特性
??? 使用ORM框架,基本上都會添加“延遲裝入”的特性支持,Entity Framework當然也不例外。當使用Entit Framework的ObjectContext與DbContext操作數據時,默認都啟用“延遲裝入”。也就是當我們在應用程序里通過LINQ to Entity查詢數據時,如果遇到關聯數據尚未裝入的情況,Entity Framework會自動幫我們再向數據庫索取關聯數據,全自動地取得關聯數據,大幅降低編寫訪問關聯數據的程序代碼。
??? 若要在Code First模型類別中啟用“延遲裝入”特性,必須在屬性聲明時加上virtual關鍵詞。
??? >>導航屬性?
??? 它是實體類上的可選屬性,它允許從關聯的一端導航到另一端。與其他屬性不同,導航屬性并不攜帶數據。