[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


posted @   Clark159  阅读(915)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示