一步步開發自己的博客 .NET版(9、從model first替換成code first 問題記錄)

来源:http://www.cnblogs.com/zhaopei/archive/2016/05/31/5540532.html
-Advertisement-
Play Games

用過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=&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 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...