• [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


    期許自己~
    能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
    真正做到「以形寫神」的境界。


  • 相关阅读:
    CSSText属性批量修改样式
    JS 学习(四)对象
    JS 学习(三)DOM
    JS语法(二)
    JS学习笔记(一)
    CSS3属性transition
    动画期间响应事件
    仿淘宝详情转场
    HTML 滚动标签<marquee>
    Oracle MySQL Server 安全漏洞
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2555975.html
Copyright © 2020-2023  润新知