用過code first的基本上都不會再想用回mode first或是db first(誰用誰知道)。不要問我為什麼不一開始就直接使用code first,因為那個時候我還不會(甚至還把mode first當成了code first)。 因為工作中使用的就是code first,且越用越習慣,越用... ...
為什麼要改用code first
用過code first的基本上都不會再想用回model first或是db first(誰用誰知道)。不要問我為什麼不一開始就直接使用code first,因為那個時候我還不會(甚至還把model first當成了code first)。
因為工作中使用的就是code first,且越用越習慣,越用越喜歡。
原因如果:
- 再也用為每次生成那個笨重的edmx文件性急了
- 再也不用當心保存tt文件而丟失特性、註銷、擴展方法了
- 再也不用為了使用微軟的驗證插件非得寫Metadata文件了
- 再也不用為了擴展tt文件生成的實體類去寫(partial)部分類了。
- 再也不用為了生成滿足自己需要的實體而去修改那些坑爹的tt文件裡面的語法代碼了(如:預設每個實體繼承一個父類)
- 再也不用為了查找edmx文件打不開,去編輯龐大的edmx文件中找那些坑爹的錯誤了。
- 等等還有些暫時沒想到的....
說改就改
修改前實體:db first(由tt文件生成)
修改後實體:code first(完全手寫)
然後把實體更新到資料庫對應的表結構。執行命令Enable-Migrations
遇到問題:
The EntityFramework package is not installed on project ''.(原因:因為沒有選擇“預設項目”)
繼續問題:
The project 'Blogs.Model' failed to build.(原因:沒有建一個繼承於DbContext的類)
ok,提示已經啟用遷移。
然後我們執行命令:Add-Migration blogs
異常: 從資料庫中獲取提供程式信息時出錯。這可能是 Entity Framework 使用的連接字元串不正確導致的。有關詳細信息,請查看內部異常並確保連接字元串正確。
我的乖乖,我非常確定我們字元串鏈接是正確的啊。
最後確定忘記給數據連接上下文在構造函數中傳入配置文件的資料庫鏈接名。
public BlogDbContext() : base("HiBlogsTest") { }
再執行(Add-Migration blogs),再出錯:
異常:無法載入指定的元數據資源。(百度之,原來是鏈接字元串有問題。http://www.cnblogs.com/chengxiaohui/articles/2106765.html)
<add name="HiBlogsTest" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient; provider connection string=" data source=.; initial catalog=HiBlogsTest; user id=sa; password=123qwe; MultipleActiveResultSets=True; App=EntityFramework"" providerName="System.Data.EntityClient" />
改成:(那一堆csdl、ssdl、msl什麼都不要了,就留個簡單的鏈接。乾凈)
<add name="HiBlogsTest" connectionString="Data Source=.;Initial Catalog=HiBlogsTest;User ID=sa;Password=123qwe;" providerName="System.Data.SqlClient" />
ok,終於沒有看見紅色的字了。
且看到了一個自動生成的blogs文件。且不管,看看資料庫是否有表結構。
空空如也。(屁都沒看到一個)(原因:BlogDbContext上下文中沒有添加實體,沒有告訴程式要生成哪些實體到資料庫)
給BlogDbContext類添加數據代碼:
public class BlogDbContext : DbContext { public BlogDbContext() : base("HiBlogsTest") { } public DbSet<BlogInfo> BlogInfos { get; set; } public DbSet<BlogComment> BlogComments { get; set; } public DbSet<BlogReadInfo> BlogReadInfos { get; set; } public DbSet<BlogTag> BlogTags { get; set; } public DbSet<BlogType> BlogTypes { get; set; } public DbSet<BlogUser> BlogUsers { get; set; } public DbSet<BlogUserInfo> BlogUserInfos { get; set; } }
然後執行 :Add-Migration blogs 再執行 update-database
終於看到表數據了。
有了表還不行,我們還沒有主外鍵。
修改BlogDbContext如下:
public class BlogDbContext : DbContext { public BlogDbContext() : base("HiBlogsTest") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); var entityBlogUser = modelBuilder.Entity<BlogUser>(); entityBlogUser.HasMany(p => p.BlogInfos).WithRequired(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")); entityBlogUser.HasRequired(p => p.BlogUserInfo).WithRequiredPrincipal(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")); entityBlogUser.HasMany(p => p.BlogTags).WithRequired(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")); entityBlogUser.HasMany(p => p.BlogTypes).WithRequired(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")); entityBlogUser.HasMany(p => p.BlogComments).WithRequired(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")); var entityBlogInfo = modelBuilder.Entity<BlogInfo>(); entityBlogInfo.HasMany(p => p.BlogTags).WithMany(t => t.BlogInfos) .Map(m => m.ToTable("BlogInfo_BlogTag")); entityBlogInfo.HasMany(p => p.BlogTypes).WithMany(t => t.BlogInfos) .Map(m => m.ToTable("BlogInfo_BlogType")); entityBlogInfo.HasMany(p => p.BlogComments).WithRequired(t => t.BlogInfo) .Map(m => m.MapKey("BlogInfoId")); entityBlogInfo.HasMany(p => p.BlogReadInfos).WithRequired(t => t.BlogInfo) .Map(m => m.MapKey("BlogInfoId")); } public DbSet<BlogInfo> BlogInfos { get; set; } public DbSet<BlogComment> BlogComments { get; set; } public DbSet<BlogReadInfo> BlogReadInfos { get; set; } public DbSet<BlogTag> BlogTags { get; set; } public DbSet<BlogType> BlogTypes { get; set; } public DbSet<BlogUser> BlogUsers { get; set; } public DbSet<BlogUserInfo> BlogUserInfos { get; set; } }View Code
然後重新命令:Add-Migration blogs 再執行 update-database
又見錯誤:
將 FOREIGN KEY 約束 'FK_dbo.BlogInfo_dbo.BlogUser_BlogUserId' 引入表 'BlogInfo' 可能會導致迴圈或多重級聯路徑。請指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 約束。
無法創建約束。請參閱前面的錯誤消息。
於是,一個一個的外鍵刪掉,又一個個的來建。終於發現:(下圖是資料庫關係圖,mssql生成的)
百度之:(原來是為了約束聯級刪除數據做的約束。實在話,還沒玩過聯級刪除了,說明這個需求應該不是很常用。找個方法禁用可否?)
直接加一個.WillCascadeOnDelete(false)就可以了。(http://www.cnblogs.com/chear/archive/2012/11/09/2762145.html)
public class BlogDbContext : DbContext { public BlogDbContext() : base("HiBlogsTest") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); var entityBlogUser = modelBuilder.Entity<BlogUser>(); entityBlogUser.HasMany(p => p.BlogInfos).WithRequired(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); //與上面等效 //modelBuilder.Entity<BlogInfo>().HasRequired(p => p.BlogUser).WithMany(t => t.BlogInfos) //以BlogUser為主表(BlogUserInfo為從表,建立外鍵) entityBlogUser.HasRequired(p => p.BlogUserInfo).WithRequiredPrincipal(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); //等效於HasRequired(p => ).WithOptional(i => ); ////以BlogUserInfo為主表(BlogUser為從表,建立外鍵) //modelBuilder.Entity<BlogUser>().HasRequired(p => p.BlogUserInfo).WithRequiredDependent(t => t.BlogUser) //.Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); //等效於 HasOptional(p => ).WithRequired(i => ); entityBlogUser.HasMany(p => p.BlogTags).WithRequired(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); entityBlogUser.HasMany(p => p.BlogTypes).WithRequired(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); entityBlogUser.HasMany(p => p.BlogComments).WithRequired(t => t.BlogUser) .Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); var entityBlogInfo = modelBuilder.Entity<BlogInfo>(); entityBlogInfo.HasMany(p => p.BlogTags).WithMany(t => t.BlogInfos) .Map(m => m.ToTable("BlogInfo_BlogTag")); entityBlogInfo.HasMany(p => p.BlogTypes).WithMany(t => t.BlogInfos) .Map(m => m.ToTable("BlogInfo_BlogType")); entityBlogInfo.HasMany(p => p.BlogComments).WithRequired(t => t.BlogInfo) .Map(m => m.MapKey("BlogInfoId")).WillCascadeOnDelete(false); entityBlogInfo.HasMany(p => p.BlogReadInfos).WithRequired(t => t.BlogInfo) .Map(m => m.MapKey("BlogInfoId")).WillCascadeOnDelete(false); } public DbSet<BlogInfo> BlogInfos { get; set; } public DbSet<BlogComment> BlogComments { get; set; } public DbSet<BlogReadInfo> BlogReadInfos { get; set; } public DbSet<BlogTag> BlogTags { get; set; } public DbSet<BlogType> BlogTypes { get; set; } public DbSet<BlogUser> BlogUsers { get; set; } public DbSet<BlogUserInfo> BlogUserInfos { get; set; } }View Code
然後重新命令:Add-Migration blogs 再執行 update-database
完美,表結構過來了。表關係過來了。(接下來就是該代碼了,因為表名做了小的改動,欄位也做了少許調整所以改的東西還真不少。整整改了一天時間。)
現在回過頭來想想,之前是先model first之後小許改動就用的db first。以前怎麼沒有遇到過(將 FOREIGN KEY 約束 'FK_dbo.BlogInfo_dbo.BlogUser_BlogUserId' 引入表 'BlogInfo' 可能會導致迴圈或多重級聯路徑。請指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 約束。
無法創建約束。請參閱前面的錯誤消息。)這個錯誤。好奇心驅使,覺得看看以前的代碼的edmx是怎麼管理這種關係的。
很驚奇的發現,完全沒有問題。於是,不死心看看資料庫裡面是不是有什麼蹊蹺。
搜噶,原來如此。通過model first生成的主外鍵關係預設就沒有設計聯級刪除,而code first預設設置就是聯級刪除。
以上內容,都是我胡說八道。謝謝您的閱讀,希望對您有那麼一點點作用。
Hi-Blogs源碼地址:http://git.oschina.net/zhaopeiym/Hi-Blogs
最近因為工作實在太慢,開源博客長久沒有更新。今天突然來回翻了好幾遍,發現半年前的自己寫的代碼是如此的不堪入目。
今天僅僅只是把db first改成了code first,發黴的代碼我還得找個時間好好重構重構。