在生产环境下处理EFCore数据库迁移的五种方法
在生產環(huán)境下處理EFCore數據庫遷移的五種方法
原文鏈接:https://www.thereformedprogrammer.net/handling-entity-framework-core-database-migrations-in-production-part-1/
作者:Jon P Smith,是《?Entity Framework Core in Action》的作者
安德魯·洛克(Andrew Lock)撰寫了精彩的系列文章《在ASP.NET Core中的應用程序啟動時運行異步任務》,其中他以“遷移數據庫”為例,介紹了您可以在啟動時執(zhí)行的操作。
在他該系列的第3部分中,他介紹了為什么在啟動時遷移數據庫并不總是最佳選擇。我決定編寫一系列有關可以安全遷移數據庫的不同方法的系列文章,即使用Entity Framework Core(EF Core)更改數據庫的架構。
這是本系列的第一部分,介紹如何創(chuàng)建遷移,而第二部分則介紹如何將遷移應用于數據庫,特別是生產數據庫。在撰寫本文時,我使用了與安德魯類似的方法,即,我嘗試使用EF Core的優(yōu)點和缺點,一路介紹創(chuàng)建遷移腳本的所有方式。
注意:Andrew和我彼此認識,因為我們同時在為Manning Publications撰寫書籍:Andrew的書是“ ASP.NET Core in Action,而我寫的書是“ Entity Framework Core in Action ”。我們分享了當作家的辛勞和喜悅,但是安德魯在ASP.NET Core方面的工作更加艱辛–他的書長700頁,而我的書“只有” 500頁。
TL; DR –創(chuàng)建遷移的摘要
注意:單擊鏈接可直接轉到涵蓋該點的部分。
?可以將兩種類型的遷移應用于數據庫:?添加新的表,列等,稱為不間斷的更改(簡單)。?更改列/表并需要復制數據,稱為重大更改(困難)。?有五種方法可以在EF Core中創(chuàng)建遷移?使用EF Core創(chuàng)建遷移-簡單,但不能處理所有可能的遷移。?使用EF Core創(chuàng)建遷移,然后手動修改遷移-中到難,但處理所有可能的遷移。?使用第三方遷移構建器來編寫C#遷移-很難,因為您需要自己編寫遷移,但是您不需要了解SQL。?使用SQL數據庫比較工具比較數據庫并輸出SQL更改腳本–很簡單,但是您確實需要對SQL有一定的了解。?通過復制EF Core的SQL來編寫自己的SQL遷移腳本 –很難理解,可以很好地控制,但您確實需要了解SQL。?如何確保您的遷移有效–使用CompareEfSql工具。
場景–關于創(chuàng)建遷移,我們應該問什么問題?
有很多遷移數據庫模式的方法,在開發(fā)中,幾乎可以使用任何方法。但是,當涉及到遷移生產數據庫(即實際用戶正在使用的數據庫)時,它就變得非常嚴重。弄錯了,至少會給您的用戶帶來不便,并且更糟的是,甚至會丟失您數據庫中的(寶貴)數據!
在獲得更新數據庫模式部分之前,我們需要構建遷移腳本,該腳本將包含模式以及可能的數據更改。要構建適當的遷移腳本,我們需要問自己一些有關需要應用到數據庫的更改類型的重要問題。所需的遷移將是:
1.一個非重大更改,也就是說,它只是增加了新的欄目,表格等,這可能而舊的軟件仍然運行應用,即舊的軟件將與遷移后的數據庫一起運行。2.一個重大更改,即有些數據必須復制或遷移過程中轉化,無法應用,而舊的軟件,即舊的軟件會遇到與遷移后的數據庫錯誤(中斷服務)。
本文中也介紹了我們正在使用EF Core,它帶來的一些好處和限制。好處是,在大多數情況下,EF Core可以自動創(chuàng)建所需的遷移。
約束條件是應用遷移后的數據庫必須與EF Core通過查看您的DbContext和映射的類建立的數據庫軟件模型匹配–我指的是帶有大寫M的EF Core模型,因為存在一個名為DbContext中的模型,其中包含類和數據庫之間的完整映射。
注意:我將介紹遷移,在這些遷移中,您可以控制映射到數據庫的類的控制和EF Core配置-有時也稱為代碼優(yōu)先方法。
我不會介紹另一種替代方法是,您直接控制數據庫,并使用稱為 scaffolding的EF Core命令為您創(chuàng)建實體類和EF Core配置。采用這種方法遷移很簡單–只需重新搭建數據庫即可。
第1部分,創(chuàng)建遷移腳本的五種方法
正如我在上一節(jié)中所述,我們創(chuàng)建的任何遷移腳本都必須將數據庫遷移到與EF Core Model匹配的狀態(tài)。例如,如果遷移在表中添加了新列,則映射到該表的實體類必須具有與該新列匹配的屬性。如果數據庫架構的EF Core的模型確實與數據庫匹配,則您可能會在查詢或寫入中發(fā)生錯誤。如果遷移腳本與該數據庫的EF Core模型匹配,則將其稱為創(chuàng)建“可用”數據庫。
毫無疑問,EF Core創(chuàng)建的遷移的有效性– EF Core創(chuàng)建了它,因此它將是有效的。但是,如果我們需要編輯遷移,或者我們自己進行遷移構建,那么我們需要非常小心,就EF Core而言,遷移會創(chuàng)建一個“可用”數據庫。這是我考慮過很多的事情。
這是創(chuàng)建遷移腳本的方法的列表。
?創(chuàng)建C#遷移腳本1.標準EF Core遷移腳本:使用EF Core的Add-Migration命令創(chuàng)建C#遷移腳本。2.手動修改的EF Core遷移腳本:使用EF Core的Add-Migration命令創(chuàng)建C#遷移腳本,然后對其進行手動編輯以添加EF Core遺漏的位。3.使用第三方遷移構建器,例如FluentMigrator。這樣的工具使您可以用C#編寫自己的遷移腳本。?創(chuàng)建SQL遷移腳本。1.使用SQL數據庫比較工具。它將最后一個數據庫架構與EF Core創(chuàng)建的新數據庫架構進行比較,并生成一個SQL腳本,該腳本會將舊數據庫遷移到新數據庫架構。2.編寫自己的SQL遷移腳本。稱職的SQL編寫者可以通過捕獲SQL EF Core用來創(chuàng)建數據庫的方式來編寫SQL遷移腳本。
這是一個摘要圖,可讓您對這五種方法進行總體回顧,并就其易用性和局限性提出個人看法。
[1]
現在,讓我們依次看一看。
1a,標準EF Core C#遷移腳本
這是EF Core提供的標準遷移技術。Microsoft官方文檔中提供了充分的文檔記錄[2],總而言之,您運行了一個名為Add-Migration的命令,該命令將三個C#文件添加到您的應用程序,其中包含使用Add-Migration 遷移現有數據庫以匹配當前EF Core設置/配置所需的更改。
| 好處 | ·自動構建遷移 ·無需學習SQL ·包括還原遷移功能 |
| 壞處 | |
| 局限性 | 標準遷移無法處理重大更改(但請參見1b)。不處理SQL功能,例如SQL用戶定義的函數(但請參見1b)。 |
| 提示 | 運行“添加遷移”方法時請注意錯誤消息。如果EF Core檢測到可能丟失數據的更改,它將輸出一條錯誤消息,但仍會創(chuàng)建遷移文件。您必須更改遷移腳本,否則將丟失數據–請參閱第1b節(jié)。·如果您的DbContext在另一個注冊了DbContext的程序集中,則需要在構建中使用MigrationsAssembly方法,并且很可能需要在DbContext程序集中實現IDesignTimeDbContextFactory。 |
| 結論 | 這是處理遷移的一種非常簡單的方法,并且在許多情況下效果很好。問題是,如果遷移無法滿足您的需求,將會發(fā)生什么情況。幸運的是,有很多方法可以解決這個問題。 |
參考:Microsoft的有關創(chuàng)建遷移的文檔[3]。
1b,手工修改的EF Core C#遷移腳本
關于EF Core的Add-Migration命令的好處是,它以C#遷移文件為起點,但是您可以自己編輯這些文件以添加代碼來處理重大更改或添加/更新數據庫的SQL部分。Microsoft提供了通過復制數據處理重大更改的示例。
| 好處 | 與標準遷移相同+ ·能夠自定義遷移。·能夠包含SQL功能,例如SQL用戶定義的功能。 |
| 壞處 | ·您需要了解數據庫中正在隱藏的內容。·可能難以決定如何編輯文件,例如,您是否保留了EF Core的所有內容,然后對其進行了更改,還是刪除了EF Core部件并自己完成了? |
| 局限性 | 沒有簡單的方法來檢查遷移是否正確(但請參閱稍后的CompareEfSql)。 |
| 提示 | 與標準遷移相同。 |
| 結論 | 非常適合進行較小的更改,但由于經常將C#命令與SQL混合使用,因此進行較大的更改可能很困難。這就是為什么我不使用EF Core遷移的原因之一。 |
參考:Microsoft手動修改遷移的示例[4]。
1c.使用第三方C#遷移構建器
安德魯·洛克(Andrew Lock)向我指出了一種使用FluentMigrator編寫遷移的方法。這與EF遷移的工作原理類似,但是您必須完成詳細說明更改的所有艱苦工作。好消息是FluentMigrator的命令非常明顯。
| 好處 | 不需要學習SQL。能明顯的看到更改了什么,即“代碼作為文檔”。 |
| 壞處 | ·您必須確定自己所做的更改。不保證產生“正確的”遷移(但請參閱稍后的CompareEfSql)。 |
| 局限性 | - 沒有 - |
| 提示 | 請注意,FluentMigrator有一個“ Migration Runners”,可以將更新應用于數據庫,但也可以輸出SQL腳本。 |
| 結論 | 我自己沒有真正的經驗。感覺這是EF Core遷移的一種更清晰的語法,但是您必須自己完成所有工作。 |
參考:GitHub的FluentMigrator[5]。
2a.使用SQL數據庫比較工具
有免費的和商業(yè)的工具可以比較兩個數據庫并創(chuàng)建一個SQL更改腳本,該腳本將舊數據庫架構遷移到新數據庫架構。
Visual Studio 2017(所有版本)中的“視圖”選項卡下內置了一個名為“SQL Server Object Explorer”的“免費”比較工具。如果右鍵單擊數據庫,則可以訪問“比較模式”工具(請參見右圖),該工具可以生成SQL更改腳本。
SQL Server的對象資源管理器工具是非常好的,但是沒有(可惜)多文檔。其他商業(yè)系統包括Redgate的SQL Compare。
| 好處 | 為您構建正確的SQL遷移腳本。 |
| 壞處 | ·您需要對數據庫有一點了解。·并非所有的SQL比較工具都生成還原腳本。 |
| 局限性 | 不處理重大更改-需要人工輸入。 |
| 提示 | 請注意SQL比較工具,該工具可以輸出日光下的所有設置,以確保設置正確。EF Core的遷移非常簡單,例如“ CREATE TABLE…”,因此應該這樣做。如果您有任何特定設置,則將它們構建到數據庫create中。 |
| 結論 | 我在難以手動編碼的大型遷移中使用了SQL Server對象資源管理器。對不熟悉SQL語言的人非常有用,尤其有用。 |
2b.手工編碼SQL遷移腳本
這聽起來確實很困難-編寫自己的SQL遷移,但是手頭上有很多幫助,無論是來自SQL比較工具(參見上文),還是查看SQL EF Core用于創(chuàng)建數據庫的幫助。這意味著我可以查看并復制以構建結論SQL遷移腳本的SQL。
| 好處 | 完全控制數據庫結構,包括EF Core不會添加的部分,例如用戶定義的函數,列約束等。 |
| 壞處 | ·您必須了解基本的SQL,如CREATE TABLE等。·您必須確定自己所做的更改(但有幫助) ·不能進行自動還原遷移。·不保證產生“正確的”遷移(但請參閱稍后的CompareEfSql)。 |
| 局限性 | - 沒有 - |
| 提示 | ·我使用一個單元測試來捕獲EF Core的確保創(chuàng)建方法的日志輸出。那讓我得到了實際的SQL EF Core輸出。然后,我尋找最后一個數據庫的差異。這使得編寫SQL遷移更加容易。? ?·通過應用所有遷移(包括新遷移)創(chuàng)建數據庫,然后運行CompareEfSql來檢查數據庫是否與EF Core的當前數據庫模型匹配,從而對遷移進行單元測試。 |
| 結論 | 這是我使用的,在CompareEfSql工具的幫助下。如果EF Core的遷移功能非常好,為什么還要處理所有這些麻煩呢?這是結論原因:·完全控制數據庫結構,包括EF Core不會添加的部分,例如用戶定義的函數,列約束等。·由于我正在編寫SQL,因此使我考慮了數據庫的各個方面。更改–該屬性是否可以為空?我需要索引嗎?等 ·通過手動修改EF Core的遷移系統來應對重大變化并非易事。我還是堅持使用SQL遷移。這是針對想要完全控制和可視化遷移的開發(fā)人員的。 |
您可以捕獲EF Core的SQL輸出以創(chuàng)建數據庫,但是可以在調用方法 EnsureCreated ( EnsureCreated 方法用于創(chuàng)建單元測試數據庫)時捕獲EF Core的日志記錄。因為為EF Core設置日志記錄有些復雜,所以我在EfCore.TestSupport庫中添加了輔助方法來處理該問題。這是一個示例單元測試,它創(chuàng)建一個新的SQL數據庫并捕獲EF Core生成的SQL命令。
[RunnableInDebugOnly] publicvoidCaptureSqlEfCoreCreatesDatabaseToConsole() { //SETUP var options = this.CreateUniqueClassOptionsWithLogging<BookContext>(log => _output.WriteLine(log.Message)); using(var context = newBookContext(options)) {//ATTEMPTcontext.Database.EnsureDeleted();context.Database.EnsureCreated(); } }讓我們看一下這段代碼的每一行
?第5行。這是一個EfCore.TestSupport方法,為您的DbContext創(chuàng)建選項。此版本使用包含類名的數據庫名稱。我這樣做是因為xUnit測試類是并行運行的,所以我想要此單元測試類的唯一數據庫。?第6行。我使用以... WithLogging結尾的選項生成器的版本,該版本允許我捕獲日志輸出。在這種情況下,我將日志的Message部分直接輸出到單元測試輸出窗口。?第11和12行。首先,我確保刪除數據庫,以便在我調用確保創(chuàng)建時,將使用由當前DbContext的配置和映射的類定義的架構來創(chuàng)建一個新的數據庫。
以下是在單元測試輸出中捕獲的部分輸出。這為您提供了EF Core用于創(chuàng)建整個架構的確切SQL。您確實只需要提取與遷移有關的部分,但是至少您可以將所需的部分剪切并粘貼到SQL遷移腳本中。
CREATE DATABASE [EfCore.TestSupport-Test_TestEfLogging]; ExecutedDbCommand(52ms) [Parameters=[], CommandType='Text', CommandTimeout='60'] IF SERVERPROPERTY('EngineEdition') <> 5 BEGINALTER DATABASE [EfCore.TestSupport-Test_TestEfLogging] SET READ_COMMITTED_SNAPSHOT ON; END; ExecutedDbCommand(5ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE TABLE [Authors] ( [AuthorId] int NOT NULL IDENTITY, [Name] nvarchar(100) NOT NULL,CONSTRAINT [PK_Authors] PRIMARY KEY ([AuthorId]) ); ExecutedDbCommand(1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE TABLE [Books] ( [BookId] int NOT NULL IDENTITY, [Title] nvarchar(256) NOT NULL, -- rest of SQL left out如何確保您的遷移有效–使用CompareEfSql工具
在創(chuàng)建遷移的描述中,我曾多次提到CompareEfSql。該工具將數據庫與EF Core首次用于DbContext時創(chuàng)建的數據庫模型進行比較。通過DbContext實例中的Model屬性訪問此模型,是通過查看DbContext配置以及DbSet和DbQuery屬性來構建結論EF Core的。
這使開發(fā)人員可以根據EF Core Model測試現有數據庫,并在錯誤消息不同的情況下為您提供錯誤消息。我發(fā)現這是一個非常強大的工具,它使我可以手動編碼SQL遷移,并確保它們是正確的有一些小限制。這是一個示例單元測試,如果數據庫架構與EF Core的模型不匹配,該測試將失敗。
[Fact] publicvoidCompareViaContext() { //SETUP var options = … options that point to the database to check; using(var context = newBookContext(options)) { var comparer = newCompareEfSql();//ATTEMPT //This will compare EF Core model of the database //with the database that the context's connection points to var hasErrors = comparer.CompareEfWithDb(context);//VERIFY //The CompareEfWithDb method returns true if there were errors. //The comparer.GetAllErrors property returns a string //where each error is on a separate linehasErrors.ShouldBeFalse(comparer.GetAllErrors); } }我喜歡這個工具,它位于EFCore.TestSupport開源庫中。它使我能夠構建遷移,并確保它們能夠正常工作。我也將其作為正常的單元測試來運行,它會立即告訴我是否是我或另一個同事更改了EF Core的設置。
您可以在名為EF Core的文章中獲得對該工具的更詳細的描述:完全控制數據庫模式及其許多功能和配置可以在CompareEfSql文檔頁面中找到[7]。
注意:我最初是為EF6.x構建此版本的(請參閱此舊文章),但是由于EF6.x并未完全公開其內部模型而受到限制。
有了EF Core,我可以做更多的事情,現在我可以檢查幾乎所有內容,并且因為我利用了EF Core的腳手架服務,所以它適用于EF Core支持的任何數據庫。
結論–第1部分
本系列的這一部分將介紹如何創(chuàng)建有效的遷移,而第二部分則涉及將遷移應用于數據庫。本文列出了使用EF Core時用于創(chuàng)建數據庫遷移的所有適用方法-優(yōu)缺點。如您所見,EF Core的Add-Migration命令確實很好,但是并不能涵蓋所有情況。
由您決定要遇到的遷移類型,以及您希望對數據庫架構進行何種級別的控制。如果您僅使用EF Core的標準遷移(1a)就可以擺脫困境,那么這將使您的生活更輕松。但是,如果您預期會發(fā)生重大變化,或者需要設置額外的SQL功能,那么您現在知道可用的選項。
令人擔心的部分出現在part2中-將遷移應用于生產數據庫。更改包含關鍵業(yè)務數據需求(需求!)的數據庫,請仔細計劃和測試。您需要考慮如果(何時!)遷移因錯誤而失敗時該怎么辦。
我放棄EF6中的EF遷移的最初原因是它在啟動時自動遷移運行良好,但它在部署時引發(fā)錯誤!而且很難找到遷移中的錯誤-僅此一項就使我遠離使用EF遷移(要獲得更多信息,可以查看這篇老文章[8])。
EF Core的遷移處理要比EF6更好:已可以實現自動遷移,并且EF Core遷移對git-merge更加友好,僅提及兩個更改。
而且,我構建SQL遷移腳本的方式使我比正在運行Add-Migration時要更加仔細地思考自己在做什么。EF Core是一個非常出色的O / RM,有時確實有許多隱藏功能。創(chuàng)建SQL遷移腳本使我從數據庫的角度考慮了遷移問題,而且我經常會對數據庫和C#代碼的一些細微調整,以使數據庫更好的運行。
References
[1]?充分的文檔記錄:?https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/
[2]?Microsoft的有關創(chuàng)建遷移的文檔:?https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/#create-a-migration
[3]?Microsoft手動修改遷移的示例:?https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/#customize-migration-code
[4]?GitHub的FluentMigrator:?https://github.com/fluentmigrator/fluentmigrator
[5]:https://www.thereformedprogrammer.net/wp-content/uploads/2019/01/SQLServerObjectExplorerCompareSchema.png
[6]?CompareEfSql文檔頁面中找到:?https://github.com/JonPSmith/EfCore.TestSupport/wiki/9.-EfSchemaCompare
[7]?老文章:?https://www.thereformedprogrammer.net/handling-entity-framework-database-migrations-in-production-part-1-applying-the-updates/
總結
以上是生活随笔為你收集整理的在生产环境下处理EFCore数据库迁移的五种方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 递归优化的这三种方式你知道吗?
- 下一篇: 初识ABP vNext(5):ABP扩展