一步步開發自己的博客 .NET版(9、從model 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=&quot;
         data source=.;
         initial catalog=HiBlogsTest;
         user id=sa;
         password=123qwe;
         MultipleActiveResultSets=True;
         App=EntityFramework&quot;" 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,發霉的代碼我還得找個時間好好重構重構。

首發地址:http://www.cnblogs.com/zhaopei/p/5540532.html 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM