EntityFramework Core 迁移忽略主外键关系
【導讀】本文來源于一位公眾號童鞋私信我的問題,在我稍加思索后給出了如下一種方案,在此之前我也思考過這個問題,借此機會我稍微看了下,目前能夠想到的也只是本文所述方案。
為何要忽略主外鍵關系
我們不僅疑惑為何要忽略主外鍵關系呢?不難想到,相對于大型企業而言大部分都會采用不建立主外鍵關系(簡稱,外鍵約束),外鍵約束毫無疑問維護了數據一致性,但對其進行操作時很容易造成問題,級聯刪除只是其一。
如果對于經常需要操作的表建立了外鍵約束,那么會嚴重影響插入、刪除和更新的性能,因為在執行這些操作之前,數據庫需要檢查其是否違反數據完整性,這也就是為何大多數不管是DBA或者架構師完全放棄使用外鍵約束的原因,在分析數據庫,它們并不能以事務方式(一次一行)來處理數據,而是批量處理,性能是一切,這是其二。
隨著業務需求變化在設計數據庫時,可能需要存儲歷史數據庫中的舊數據,而這些舊數據可能對數據質量和完整性沒有嚴格要求。為了能夠容納舊的臟數據,可直接清理和轉換舊數據,而放棄在數據庫級別上強制執行參照完整性,這是其三。
所以基于以上幾點理由,忽略外鍵約束是有其原因所在,當然,是否放棄外鍵約束,可能取決于架構師或者DBA,反正決策權不在于搬磚的我們,我們知道其原因就好。
示例程序
以下示例皆在控制臺中進行,老規矩,我們先給出示例模型,依然是Blog和Post兩個實體,如下:
public?class?Blog {public?int?Id?{?get;?set;?}public?string?Name?{?get;?set;?}public?List<Post>?Posts?{?get;?set;?} }public?class?Post {public?int?Id?{?get;?set;?}public?string?Title?{?get;?set;?}public?int?BlogId?{?get;?set;?}public?Blog?Blog?{?get;?set;?} }接下來則是定義上下文,如下:
public?class?EFCoreDbContext?:?DbContext {public?DbSet<Blog>?Blogs?{?get;?set;?}public?DbSet<Post>?Posts?{?get;?set;?}protected?override?void?OnConfiguring(DbContextOptionsBuilder?optionsBuilder){optionsBuilder.UseSqlServer("Server=.;Database=EFCore;Trusted_Connection=True;");} }忽略外鍵約束
上述即使我們沒有顯式通過注解或Fluent APi配置關系,但是會根據約定而發現其關系,所以最終會建立外鍵約束,那么我們怎么才能在遷移時不建立外鍵約束呢?對依賴實體通過注解顯式配置不映射,如下:
[NotMapped] public?List<Post>?Posts?{?get;?set;?}請注意,這里必須是對依賴實體進行顯式注解不映射,若是對依賴實體上的主體導航屬性配置依然會生成外鍵約束,若是對外鍵進行注解不映射也是同理,只不過生成的外鍵名稱和默認的外鍵名稱不一樣而已。
很顯然,進行如上不建立外鍵約束后,當我們通過主體添加依賴體數據時將不會持久化到表中,比如如下通過Blog添加Posts
同理,當通過主體進行饑餓加載時將會拋出異常(無論是lambda表達式或字符串),比如如下,因為二者已經沒有任何關聯關系
基于上述,似乎沒有什么很好的方式,只能采用最原始方式生成外鍵約束后,在遷移類中刪除外鍵約束或數據庫表手動刪除外鍵約束,這樣仍然可以很好的使用饑餓加載導航屬性。
一旦實體比較多,手動刪除又顯得比較麻煩,我們可以寫個程序,當遷移完畢后刪除數據庫表所有外鍵,如下截取刪除外鍵的代碼片段,不知是否行得通,理論上應該是可以實現的。
????public?static?class?RemoveForeignKeyExetension{public?static?ModelBuilder?RemoveForeignKeys(this?ModelBuilder?modelBuilder){var?entityTypes?=?modelBuilder.Model.GetEntityTypes().ToList();for?(int?i?=?0;?i?<?entityTypes.Count();?i++){var?entityType?=?entityTypes[i];var?references?=?entityType.GetDeclaredReferencingForeignKeys().ToList();using?(((Model)entityType.Model).Builder.Metadata.ConventionDispatcher.DelayConventions()){foreach?(var?reference?in?references){reference.DeclaringEntityType.RemoveForeignKey(reference);}}}return?modelBuilder;}}忽略外鍵約束(SQLite)
上述是針對SQL Server所做的測試,理論上MySQL同理,但對于SQLite數據庫,EF Core 3.x提供了全局方案:通過數據連接字符串配置【Foreign Keys = False】全局抑制建立外鍵約束。
optionsBuilder.UseSqlite("Database=sqlite.db;Foreign?Keys=False");官方團隊好像并未提供針對SQL Server或MySQL忽略而不建立外鍵約束而可以加載導航屬性的辦法,只能采取笨拙或者如上所述寫個程序去刪除外鍵約束或者通過注解方式實現,但是一旦使用注解將無法加載導航屬性,那么用EF Core就失去了很大的意義,我認為
總結
以上是生活随笔為你收集整理的EntityFramework Core 迁移忽略主外键关系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ERP的配置管理实践
- 下一篇: 你很可能需要知道这个调试小技巧