全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)
在開發涉及到數據庫的程序時,常會遇到一開始設計的結構不能滿足需求需要再添加新字段或新表的情況,這時就需要進行數據庫遷移。
實現數據庫遷移有很多種辦法,從手動管理各個版本的ddl腳本,到實現自己的migrator,或是使用Entity Framework提供的Code First遷移功能。
Entity Framework提供的遷移功能可以滿足大部分人的需求,但仍會存在難以分項目管理遷移代碼和容易出現"context has changed"錯誤的問題。
這里我將介紹ZKWeb網頁框架在Fluent NHibernate和Entity Framework Core上使用的辦法。
可以做到添加實體字段后,只需刷新網頁就可以把變更應用到數據庫。
實現全自動遷移的思路
數據庫遷移需要指定變更的部分,例如添加表和添加字段。
而實現全自動遷移需要自動生成這個變更的部分,具體來說需要
獲取數據庫現有的結構
獲取代碼中現有的結構
對比結構之間的差異并生成遷移
這正是Entity Framework的Add-Migration(或dotnet ef migrations add)命令所做的事情,
接下來我們將看如何不使用這類的命令,在NHibernate, Entity Framework和Entity Framework Core中實現全自動的處理。
Fluent NHibernate的全自動遷移
ZKWeb框架使用的完整代碼可以查看這里
首先Fluent NHibernate需要添加所有實體的映射類型,以下是生成配置和添加實體映射類型的例子。
配置類的結構可以查看這里
接下來是把所有實體的結構添加或更新到數據庫。
NHibernate提供了SchemaUpdate,這個類可以自動檢測數據庫中是否已經有表或字段,沒有時自動添加。
使用辦法非常簡單,以下是使用的例子
到這一步就已經實現了全自動遷移,但我們還有改進的余地。
因為SchemaUpdate不保存狀態,每次都要檢測數據庫中的整個結構,所以執行起來EF的遷移要緩慢很多,
ZKWeb框架為了減少每次啟動網站的時間,在執行更新之前還會檢測是否需要更新。
這段代碼使用了SchemaExport來生成所有表的DDL腳本,生成后和上次的生成結果對比,不一致時才調用SchemaUpdate更新。
NHibernate提供的自動遷移有以下的特征,使用時應該注意
字段只會添加,不會刪除,如果你重命名了字段原來的字段也會保留在數據庫中
字段類型如果改變,數據庫不會跟著改變
關聯的外鍵如果改變,遷移時有可能會出錯
總結NHibernate的自動遷移只會添加表和字段,基本不會修改原有的結構,有一定的限制但是比較安全。
Entity Framework的全自動遷移
ZKWeb框架沒有支持Entity Framework 6,但實現比較簡單我就直接上代碼了。
例子
Entity Framework提供的自動遷移有以下的特征,使用時應該注意
如果字段重命名,舊的字段會被刪除掉,推薦做好數據的備份和盡量避免重命名字段
外鍵關聯和字段類型都會自動變化,變化時有可能會導致原有的數據丟失
自動遷移的記錄和使用工具遷移一樣,都會保存在__MigrationHistory表中,切勿混用否則代碼將不能用到新的數據庫中
總結Entity Framework的遷移可以保證實體和數據庫之間很強的一致性,但是使用不當會導致原有數據的丟失,請務必做好數據庫的定時備份。
Entity Framework Core的全自動遷移
Entity Framework Core去掉了SetInitializer選項,取而代之的是DatabaseFacade.Migrate和DatabaseFacade.EnsureCreated。
DatabaseFacade.Migrate可以應用使用ef命令生成的遷移代碼,避免在生產環境中執行ef命令。
DatabaseFacade.EnsureCreated則從頭創建所有數據表和字段,但只能創建不能更新,不會添加紀錄到__MigrationHistory。
這兩個函數都不能實現全自動遷移,ZKWeb框架使用了EF內部提供的函數,完整代碼可以查看這里
Entity Framework Core的自動遷移實現比較復雜,我們需要分兩步走。
第一步 創建遷移記錄__ZKWeb_MigrationHistory表,這個表和EF自帶的結構相同,但這個表是給自己用的不是給ef命令用的
第二部 查找最后一條遷移記錄,和當前的結構進行對比,找出差異并更新數據庫
第一步的代碼使用了EnsureCreated創建數據庫和遷移記錄表,其中EFCoreDatabaseContextBase只有遷移記錄一個表。
創建完以后還要把帶遷移記錄的結構保留下來,用作后面的對比,如果這里不保留會導致遷移記錄的重復創建錯誤。
在執行第二步之前,還需要先判斷連接的數據庫是不是關系數據庫,
因為Entity Framework Core以后還會支持redis mongodb等非關系型數據庫,自動遷移只應該用在關系數據庫中。
第二步需要查找最后一條遷移記錄,和當前的結構進行對比,找出差異并更新數據庫。
先看遷移記錄表的內容,遷移記錄表中有三個字段
Revision 每次遷移都會+1
Model 當前的結構,格式是c#代碼
ProductVersion 遷移時Entity Framework Core的版本號
Model存放的代碼例子如下,這段代碼記錄了所有表的所有字段的定義,是自動生成的。
后面我將會講解如何生成這段代碼。
接下來查找最后一條遷移記錄:
var lastModel = initialModel;var histories = context.Set<EFCoreMigrationHistory>();var lastMigration = histories.OrderByDescending(h => h.Revision).FirstOrDefault();存在時,編譯Model中的代碼并且獲取ModelSnapshot.Model的值,這個值就是上一次遷移時的完整結構。
不存在時,將使用initialModel的結構。
編譯使用的是另外一個組件,你也可以用Roslyn CSharp Scripting包提供的接口編譯。
和當前的結構進行對比:
// Compare with the newest modelvar modelDiffer = serviceProvider.GetService<IMigrationsModelDiffer>();var sqlGenerator = serviceProvider.GetService<IMigrationsSqlGenerator>();var commandExecutor = serviceProvider.GetService<IMigrationCommandExecutor>();var operations = modelDiffer.GetDifferences(lastModel, context.Model);if (operations.Count <= 0) { ? ?// There no differencereturn; }如果有差異,生成遷移命令(commands)和當前完整結構的快照(modelSnapshot)。
上面Model中的代碼由這里的CSharpMigrationsGenerator生成,modelSnapshot的類型是string。
插入遷移記錄并執行遷移命令:
// Insert the history first, if migration failed, delete itvar history = new EFCoreMigrationHistory(modelSnapshot); histories.Add(history); context.SaveChanges();try { ? ?// Execute migration commandscommandExecutor.ExecuteNonQuery(commands, connection); } catch {histories.Remove(history);context.SaveChanges(); ? ?throw; }到這里就完成了Entity Framework Core的自動遷移,以后每次有更新都會對比最后一次遷移時的結構并執行更新。
Entity Framework Core的遷移特點和Entity Framework一樣,可以保證很強的一致性但需要注意防止數據的丟失。
寫在最后
全自動遷移數據庫如果正確使用,可以增強項目中各個模塊的獨立性,減少開發和部署的工作量。
但是因為不能手動控制遷移內容,有一定的局限和危險,需要了解好使用的ORM遷移的特點。
寫在最后的廣告
ZKWeb網頁框架已經在實際項目中使用了這項技術,目前來看遷移部分還是比較穩定的。
這項技術最初是為了插件商城而開發的,在下載安裝插件以后不需要重新編譯主程序,不需要執行任何遷移命令就能使用。
目前雖然沒有實現插件商城,也減少了很多日常開發的工作。
如果你有興趣,歡迎加入ZKWeb交流群522083886共同探討。
原文地址:http://www.cnblogs.com/zkweb/p/5859536.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 沉沦17年,这位昔日科技霸主、最值钱企业
- 下一篇: ZKWeb网站框架的动态编译的实现原理