[Architecture Pattern] Database Migration (下)
接续...
[Architecture Pattern] Database Migration (上)
实作
范列下载
实作说明请参照范例程序内容:DatabaseMigrationSample点此下载
范列实作
首先建立封装数据库更新逻辑的DatabaseUpdater,以及定义DatabaseUpdater会使用到的各个接口。执行DatabaseUpdater的Update方法,系统会依照数据库更新逻辑,执行对应的DatabaseUpdatePlan。并且反复执行,直到数据库版本为目前DatabaseUpdatePlan能更新的最新版本。
1 2 3 4 5 6 7 | public interface IDatabaseVersionManager { // Methods string GetCurrentDatabaseVersion(); void SetCurrentDatabaseVersion( string databaseVersion); } |
1 2 3 4 5 | public interface IDatabaseUpdatePlanRepository { // Methods IEnumerable<IDatabaseUpdatePlan> GetAllUpdatePlan(); } |
1 2 3 4 5 6 7 8 9 10 11 | public interface IDatabaseUpdatePlan { // Properties string TargetDatabaseVersion { get ; } string ResultDatabaseVersion { get ; } // Methods void Execute(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | public class DatabaseUpdater { // Fields private readonly IDatabaseVersionManager _databaseVersionManager = null ; private readonly IDatabaseUpdatePlanRepository _databasePlanRepository = null ; // Constructor public DatabaseUpdater(IDatabaseVersionManager databaseVersionManager, IDatabaseUpdatePlanRepository databasePlanRepository) { #region Contracts if (databaseVersionManager == null ) throw new ArgumentNullException(); if (databasePlanRepository == null ) throw new ArgumentNullException(); #endregion _databaseVersionManager = databaseVersionManager; _databasePlanRepository = databasePlanRepository; } // Methods public void Update() { for (; ; ) { if ( this .UpdateDatabase() == false ) { return ; } } } private bool UpdateDatabase() { // Get DatabaseVersion string databaseVersion = this .GetDatabaseVersion(); // Get DatabaseUpdatePlan IDatabaseUpdatePlan databaseUpdatePlan = this .GetDatabaseUpdatePlan(databaseVersion); if (databaseUpdatePlan == null ) return false ; // Execute DatabaseUpdatePlan databaseUpdatePlan.Execute(); databaseVersion = databaseUpdatePlan.ResultDatabaseVersion; // Set DatabaseVersion this .SetDatabaseVersion(databaseVersion); // Return return true ; } private string GetDatabaseVersion() { string databaseVersion = _databaseVersionManager.GetCurrentDatabaseVersion(); if (databaseVersion == null ) databaseVersion = string .Empty; return databaseVersion; } private void SetDatabaseVersion( string databaseVersion) { #region Contracts if ( string .IsNullOrEmpty(databaseVersion) == true ) throw new ArgumentException(); #endregion _databaseVersionManager.SetCurrentDatabaseVersion(databaseVersion); } private IDatabaseUpdatePlan GetDatabaseUpdatePlan( string databaseVersion) { #region Contracts if (databaseVersion == null ) throw new ArgumentNullException(); #endregion foreach (IDatabaseUpdatePlan databaseUpdatePlan in _databasePlanRepository.GetAllUpdatePlan()) { if (databaseUpdatePlan.TargetDatabaseVersion == databaseVersion) { return databaseUpdatePlan; } } return null ; } } |
接着实作封装数据库版本管理职责的DatabaseVersionManager。实作范例中是将数据库版本的数据,存放到数据库中,由数据库描述自己目前的版本。并且DatabaseVersionManager在取得版本数据的时候,会特别检查数据库是否已经建立,这是用来处理建立第1.0版数据库的相关逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | public class SqlDatabaseVersionManager : IDatabaseVersionManager { // Fields private readonly string _connectionString = null ; // Constructor public SqlDatabaseVersionManager( string connectionString) { #region Contracts if ( string .IsNullOrEmpty(connectionString) == true ) throw new ArgumentException(); #endregion _connectionString = connectionString; } // Methods public string GetCurrentDatabaseVersion() { // Check Database Exist if (( int )(SqlHelper.ExecuteScalar(_connectionString, @"SELECT COUNT(*) FROM sysdatabases WHERE name = N'MyDatabase'" )) == 0) { return string .Empty; } // Get DatabaseVersion string databaseVersion = ( string )(SqlHelper.ExecuteScalar(_connectionString, @"SELECT CurrentVersion FROM [MyDatabase].[dbo].[DatabaseVersion]" )); if (databaseVersion == null ) databaseVersion = string .Empty; // Return return databaseVersion; } public void SetCurrentDatabaseVersion( string databaseVersion) { #region Contracts if ( string .IsNullOrEmpty(databaseVersion) == true ) throw new ArgumentException(); #endregion // Clear DatabaseVersion SqlHelper.ExecuteScalar(_connectionString, @"DELETE FROM [MyDatabase].[dbo].[DatabaseVersion]" ); // Set DatabaseVersion SqlHelper.ExecuteScalar(_connectionString, @"INSERT INTO [MyDatabase].[dbo].[DatabaseVersion](CurrentVersion) VALUES('" + databaseVersion + "')" ); } } |
接着建立第1.0版的DatabaseUpdatePlan。DatabaseUpdatePlan里,封装了目标版本、Schema变更、数据转换…等等职责。在第1.0版的DatabaseUpdatePlan最主要是为系统数据库加入了User这个数据表。并且第1.0版的DatabaseUpdatePlan跟其他版本的DatabaseUpdatePlan会有一个主要的差异在于:第1.0版的DatabaseUpdatePlan另外还要负责建立系统使用的数据库(MyDatabase)、记录数据库版本的数据表(DatabaseVersion)…等等必要设定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | public class SqlDatabaseUpdatePlanV1_0 : IDatabaseUpdatePlan { // Fields private readonly string _connectionString = null ; // Constructor public SqlDatabaseUpdatePlanV1_0( string connectionString) { #region Contracts if ( string .IsNullOrEmpty(connectionString) == true ) throw new ArgumentException(); #endregion _connectionString = connectionString; } // Properties public string TargetDatabaseVersion { get { return string .Empty; } } public string ResultDatabaseVersion { get { return @"MyDatabase V1.0" ; } } // Methods public void Execute() { // Create Database SqlHelper.ExecuteNonQuery(_connectionString, @"CREATE DATABASE MyDatabase" ); // Add DatabaseVersion Table SqlHelper.ExecuteNonQuery(_connectionString, @"CREATE TABLE [MyDatabase].[dbo].[DatabaseVersion] (CurrentVersion nvarchar(50))" ); // Add User Table SqlHelper.ExecuteNonQuery(_connectionString, @"CREATE TABLE [MyDatabase].[dbo].[User] (UserId nvarchar(50), UserName nvarchar(50))" ); } } |
接着建立第1.3版的DatabaseUpdatePlan。在第1.3版的DatabaseUpdatePlan最主要是为系统数据库加入了Company这个数据表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class SqlDatabaseUpdatePlanV1_3 : IDatabaseUpdatePlan { // Fields private readonly string _connectionString = null ; // Constructor public SqlDatabaseUpdatePlanV1_3( string connectionString) { #region Contracts if ( string .IsNullOrEmpty(connectionString) == true ) throw new ArgumentException(); #endregion _connectionString = connectionString; } // Properties public string TargetDatabaseVersion { get { return @"MyDatabase V1.0" ; } } public string ResultDatabaseVersion { get { return @"MyDatabase V1.3" ; } } // Methods public void Execute() { // Add Order Table SqlHelper.ExecuteNonQuery(_connectionString, @"CREATE TABLE [MyDatabase].[dbo].[Company] (CompanyId nvarchar(50), CompanyName nvarchar(50))" ); } } |
再来建立封装更新计划管理职责的DatabaseUpdatePlanRepository。在范例程序中,为了简化所以采用Hard Code的方式,来管理DatabaseUpdatePlan。实际的项目中可以采用各种DI framework来完成DatabaseUpdatePlan生成注入的工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class SqlDatabaseUpdatePlanRepository : IDatabaseUpdatePlanRepository { // Fields private readonly string _connectionString = null ; // Constructor public SqlDatabaseUpdatePlanRepository( string connectionString) { #region Contracts if ( string .IsNullOrEmpty(connectionString) == true ) throw new ArgumentException(); #endregion _connectionString = connectionString; } // Methods public IEnumerable<IDatabaseUpdatePlan> GetAllUpdatePlan() { IDatabaseUpdatePlan[] databaseUpdatePlanArray = new IDatabaseUpdatePlan[] { new SqlDatabaseUpdatePlanV1_0(_connectionString), new SqlDatabaseUpdatePlanV1_3(_connectionString), }; return databaseUpdatePlanArray; } } |
最后执行生成建立DatabaseUpdater的ConsoleApplication程序。就可以看到原本空白的数据库,建立了系统必要的数据库、数据表。而检查程序建立的数据表内容,可以发现第1.3版本才加入的Company数据表,这也就是说数据库已经升级成为了第1.3版的数据库。(记得重新整理,才能看到更新资料)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | class Program { static void Main( string [] args) { // Setting string connectionString = @"Data Source=CLARK-HOME\SQLEXPRESS;Integrated Security=True" ; // Create DatabaseUpdater databaseUpdater = CreateDatabaseUpdater(connectionString); // Update databaseUpdater.Update(); // End Console.WriteLine( "End..." ); Console.ReadLine(); } static DatabaseUpdater CreateDatabaseUpdater( string connectionString) { #region Contracts if ( string .IsNullOrEmpty(connectionString) == true ) throw new ArgumentException(); #endregion // DatabaseVersionManager IDatabaseVersionManager databaseVersionManager = new SqlDatabaseVersionManager(connectionString); // DatabasePlanRepository IDatabaseUpdatePlanRepository databasePlanRepository = new SqlDatabaseUpdatePlanRepository(connectionString); // DatabaseUpdater DatabaseUpdater databaseUpdater = new DatabaseUpdater(databaseVersionManager, databasePlanRepository); // Return return databaseUpdater; } } |
到目前为止,范例程序验证了从无到有的生成数据库。接下来的范例程序,用来示范升级数据库版本的功能。首先建立第2.0版的DatabaseUpdatePlan。在第2.0版的DatabaseUpdatePlan最主要是为系统数据库加入了Order这个数据表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class SqlDatabaseUpdatePlanV2_0 : IDatabaseUpdatePlan { // Fields private readonly string _connectionString = null ; // Constructor public SqlDatabaseUpdatePlanV2_0( string connectionString) { #region Contracts if ( string .IsNullOrEmpty(connectionString) == true ) throw new ArgumentException(); #endregion _connectionString = connectionString; } // Properties public string TargetDatabaseVersion { get { return @"MyDatabase V1.3" ; } } public string ResultDatabaseVersion { get { return @"MyDatabase V2.0" ; } } // Methods public void Execute() { // Add Order Table SqlHelper.ExecuteNonQuery(_connectionString, @"CREATE TABLE [MyDatabase].[dbo].[Order] (OrderId nvarchar(50), OrderName nvarchar(50))" ); } } |
接着将建立完成的第2.0版DatabaseUpdatePlan加入DatabaseUpdatePlanRepository。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class SqlDatabaseUpdatePlanRepository : IDatabaseUpdatePlanRepository { // Fields private readonly string _connectionString = null ; // Constructor public SqlDatabaseUpdatePlanRepository( string connectionString) { #region Contracts if ( string .IsNullOrEmpty(connectionString) == true ) throw new ArgumentException(); #endregion _connectionString = connectionString; } // Methods public IEnumerable<IDatabaseUpdatePlan> GetAllUpdatePlan() { IDatabaseUpdatePlan[] databaseUpdatePlanArray = new IDatabaseUpdatePlan[] { new SqlDatabaseUpdatePlanV1_0(_connectionString), new SqlDatabaseUpdatePlanV1_3(_connectionString), new SqlDatabaseUpdatePlanV2_0(_connectionString), }; return databaseUpdatePlanArray; } } |
最后执行先前建立的ConsoleApplication程序。就可以看到原本第1.3版的数据库内,加入第2.0版的Order数据表,这也就是说数据库已经升级成为了第2.0版的数据库。(记得重新整理,才能看到更新资料)
后记
碍于篇幅的关系,DatabaseUpdater简化了很多的功能设计,例如:在DatabaseUpdater加入数据库的备份与还原功能,以提高系统的可用性。或者是在DatabaseUpdater里加入升级到特定版本的功能,以增加工具的重用性。还有其他一些重要的功能,这些功能都有很大的用处。开发人员在实际项目的建置时,可以视项目需求来加入实作。
另外不管是微软产品还是其他软件产品,都有与Database Migration功能相近的软件实作。
相关数据可以参考:
-Entity Framework - 使用Code First的Enabling Migrations(Code-Base)
-在Django里做Database Migration
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?