Code First
Code First中文可以稱「程式碼先行」或「程式碼優先」,在Entity Framework 4.1時正式支援,它與一般開發習慣不同,先透過程式碼來定義資料結構(一般是POCO,Plain Old CLR Object),定義完成之後即可開始進行開發工作。在開發進行過程,開發人員不用管資料庫之類的問題,Entity Framework會進行處理,開發人員只要專注處理POCO與邏輯程式碼身上。
多對多關係?
多對多關係(Many-To-Many)是指資料兩者之間的關係是多對多,例如,醫生與病人,一個醫生會醫治多個病人,一個病人依病情不同也會有多個醫生來治療(會診即是一種),這種關係在資料庫領域稱多對多關係(Many-to-Many)。
有一點點資料庫經驗的人員都知道,實務上在資料庫中是沒有多對多資料表,多對多關係到了資料庫裡會化成三張資料表,分別建立成one-to-many與one-to-many的關係,以上例說明:「醫生(one)-to-醫生病人(many)」與「病人(one)-to-醫生病人(many)」,所以會有「醫生資料表」「醫生病人資料表」「病人資料表」三張資料表,中間透過「醫生病人資料表」來串接多對多關係。
ASP.NET MVC實作Code First Many-to-Many Relationship
以下我們透過ASP.NET MVC [4|5]專案來實作一次Code First的多對多關係。我們模擬的關係是「開發人員-to-專案」,一位開發人員可以同時接手多個專案,一個專案同時有多個開發人員進行開發。
- 新增ASP.NET MVC [4|5]專案。
MVC 4請將AccountController.cs、AccountModels.cs、InitializeSimpleMembershipAttribute.cs相關類別移除或註解。
MVC 5請將AccountController.cs、AccountViewModels.cs、IdentityModels.cs移除或註解。 - 在Models新增Dev類別,代表開發人員。
public class Dev { public Dev() { Projects = new HashSet<Project>(); } public int DevId { get; set; } public string DevName { get; set; } public ICollection<Project> Projects { get; set; } }
注意ICollection<>,它代表的是多的關係。 - 在Models新增Project類別,代表專案。
public class Project { public Project() { Devs = new HashSet<Dev>(); } public int ProjectId { get; set; } public string ProjectName { get; set; } public ICollection<Dev> Devs { get; set; } }
注意ICollection<>,它代表的是多的關係。 - 在Models新增DevProjectContext類別,進行Code First的DbContent設定。
using System.Data.Entity; public class DevProjectContext : DbContext { public DevProjectContext() : base("DefaultConnection") { } public DbSet<Dev> Dev { get; set; } public DbSet<Project> Project { get; set; } }
base("DefaultConnection")裡的DefaultConnection請查詢web.config,這裡是要將資料表儲存至SqlLocalDb裡的意思。 - 到/Home/Index裡撰寫一些簡單的程式碼:
public ActionResult Index() { var p1 = new Project() { ProjectName = "Web API 2" }; var p2 = new Project() { ProjectName = "MVC 5" }; var p3 = new Project() { ProjectName = "Bootstrap 3" }; var e1 = new Dev() { DevName = "Bruce" }; var e2 = new Dev() { DevName = "Sherry" }; var e3 = new Dev() { DevName = "Happy" }; p1.Devs.Add(e1); p1.Devs.Add(e2); p2.Devs.Add(e2); p2.Devs.Add(e3); p3.Devs.Add(e3); p3.Devs.Add(e1); using (var ctx = new DevProjectContext()) { ctx.Project.Add(p1); ctx.Project.Add(p2); ctx.Project.Add(p3); ctx.SaveChanges(); } return View(); }
- 執行應用程式,第一次要建立新資料庫所以啟動的時間會比平常久一些些。
Code First產生資料表
在上面範例中,我們定義好相關POCO類別後,立即/Home/Index裡即開始進行開發動作,啟動應用程式後,來看看我們最關心的資料表。
在App_Data中會產生預設的SqlLocalDb檔案(預設不會顯示),左是MVC 4,右是MVC 5:
點擊進入之後可以看到多對多的三張資料表(左是MVC 4,右是MVC 5,MVC 5會多一張記錄異動的資料表,但那是額外的,重點是那三張資料表):
範例照打不會動喔
回覆刪除該不會是故意的吧 哈哈
等一下會不會有人 mail 來要程式碼 XD
Devs 中 s 的問題,有些地方有 s,有些地方沒有 s
已修正 Dev.cs / Project.cs 的內容。謝謝。
刪除您好,
回覆刪除最近在實作時,在1對多或多對多關係的查詢上有點疑問。
在實作上,都會使用ICollection來宣告子項目,但ICollection並不是擴充於IQueryable。
而ODATA或LINQ子查詢使用IQueryable的項目會將所有查詢統合到SQL,理論上會有更好的效能。
但使用ICollection子項目的話,會使用完整的SQL Command去撈回所有子項目,之後才在記憶體中去處理子查詢/排序。
例如,我想撈出某Dev的所有未關閉的Project、並以建立時間排序:
var dev = ctx.Dev.Find(id);
return dev.Projects.Where(proj => !proj.IsClose).OrderBy(proj => proj.CreatedOn);
我監視了一下,在SQL端的確使用INNER JOIN撈出了所有屬於該dev的Project。
那麼,篩選與排序就是在Server上做的。
這樣應該會造成不必要的效能(Web Server)和頻寬(SQL Server與Web Server之間)負擔?
有辦法能解決嗎?
還是說不需要擔心這點?
Entity Framework 的效能調效是個大主題。
刪除基本認知是:EF的方便性是用效能換來的,如果真的非常非常Care效能,那就走ADO.NET方式不要走Entity Framework。
「entity framework 效能」與「entity framework performance」關鍵字有些不錯的文章可以參考。
不過,EF 5之後,預設會採用“預編譯”、“快取”…等方式來加速,當然,“第一次”還是會慢一些,不過後面的重覆性查詢,有時可以得到幾乎ADO.NET一樣快的結果。