16

我正在使用 TFS 发布管理进行持续集成和部署。

我在部署期间使用 migrate.exe 执行数据库迁移,当您从旧版本升级到新版本时,这非常有用。但是,当您想要部署旧版本的应用程序时,它会变得更加混乱。

基本上,为上下文保存迁移的程序集必须知道如何从版本 3 转到版本 2。通常,您使用要部署的程序集作为迁移的源,但在这种情况下,您必须使用已经部署的程序集,因为它们是唯一知道如何从 v3 到 v2 的程序集。(版本 2 甚至不知道 v3 存在。)

我目前的计划是在部署期间以某种方式比较这两个程序集。如果安装目录中的程序集包含比部署目录中的“更新”迁移,我首先需要在部署目录中的程序集中获取“最新”可用迁移,然后执行:

migrate.exe AssemblyInInstallationDir /targetMigration NewestFromAssemblyInDeploymentDir

在升级到较新版本的“正常”部署场景中,您可以这样做:

migrate.exe AssemblyInDeploymentDir

这是一种合法的方法吗?我还没有考虑使用 EF 库来评估每个程序集中可用的迁移。还有一个挑战是这些程序集中的每一个都是“相同的”只是不同的版本。我可能必须将它们加载到单独的应用程序域中,然后使用跨应用程序域通信来获取我需要的信息。

编辑

我创建了一个概念验证应用程序,它允许我列出对同一程序集的两个不同版本的可用迁移。这对整个过程至关重要,所以我认为值得记录。

该应用程序使用反射来加载每个程序集,然后使用 System.Data.Entity.Migrations 中的 DbMigrator 类来枚举迁移元数据。迁移的名称以时间戳信息为前缀,因此我可以对它们进行排序并查看哪个程序集包含“较新”的迁移集。

static void Main(string[] args)
{
    const string dllName = "Test.Data.dll";
    var assemblyCurrent = Assembly.LoadFile(Path.Combine(System.Environment.CurrentDirectory, string.Format("Current\\{0}", dllName)));
    var assemblyTarget = Assembly.LoadFile(Path.Combine(System.Environment.CurrentDirectory, string.Format("Target\\{0}", dllName)));

    Console.WriteLine("Curent Version: " + assemblyCurrent.FullName);
    Console.WriteLine("Target Version: " + assemblyTarget.FullName);

    const string contextName = "Test.Data.TestContext";
    const string migrationsNamespace = "Test.Data.Migrations";
    var currentContext = assemblyCurrent.CreateInstance(contextName);
    var targetContext = assemblyTarget.CreateInstance(contextName);

    var currentContextConfig = new DbMigrationsConfiguration
    {
        MigrationsAssembly = assemblyCurrent,
        ContextType = currentContext.GetType(),
        MigrationsNamespace = migrationsNamespace
    };

    var targetContextConfig = new DbMigrationsConfiguration
    {
        MigrationsAssembly = assemblyTarget,
        ContextType = targetContext.GetType(),
        MigrationsNamespace = migrationsNamespace
    };

    var migrator = new DbMigrator(currentContextConfig);
    var localMigrations = migrator.GetLocalMigrations(); //all migrations

    Console.WriteLine("Current Context Migrations:");
    foreach (var m in localMigrations)
    {
        Console.WriteLine("\t{0}", m);
    }

    migrator = new DbMigrator(targetContextConfig);
    localMigrations = migrator.GetLocalMigrations(); //all migrations

    Console.WriteLine("Target Context Migrations:");
    foreach (var m in localMigrations)
    {
        Console.WriteLine("\t{0}", m);
    }

    Console.ReadKey();
}

}

应用程序的输出如下所示:

Curent Version: Test.Data, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null
Target Version: Test.Data, Version=1.2.0.0, Culture=neutral, PublicKeyToken=null

Current Context Migrations:
    201403171700348_InitalCreate
    201403171701519_AddedAddresInfoToCustomer
    201403171718277_RemovedStateEntity
    201403171754275_MoveAddressInformationIntoContactInfo
    201403181559219_NotSureWhatIChanged
    201403181731525_AddedRowVersionToDomainObjectBase
Target Context Migrations:
    201403171700348_InitalCreate
    201403171701519_AddedAddresInfoToCustomer
    201403171718277_RemovedStateEntity
4

2 回答 2

3

我们实际上解决了这个问题,并且已经使用我们的工具一年多来将完全连续的数据库部署到生产环境中。没有人类参与。:)

我们在 GitHub 上公开了其中的一些内容:https ://github.com/GalenHealthcare/Galen.Ef.Deployer

您可以进行“重大”更改,但通常我们也会避免这种情况 - 但主要是因为我们的应用程序在升级期间仍然有效。我们将数据层视为可独立部署的组件 - 因此,它具有需要保持兼容的“接口”。

我们通常会使用多阶段升级方法,我们部署一个向后/向前兼容的中间版本,升级我们的各种应用程序服务,然后最后升级数据库层以消除遗留兼容性。

即使在这种情况下,我们也能够自动从/到我们的架构和数据的任何版本。事实上,我们已经添加了单元测试,每次我们为每个数据库版本构建时都会验证这一点。它基本上沿着模式迭代链向上/向下走,并验证向上和向下迁移始终有效并保持数据一致性和兼容性。您可以在 GitHub 项目中看到这些测试。这是一个例子:

https://github.com/GalenHealthcare/Galen.Ef.Deployer/blob/master/Galen.Ci.EntityFramework.Deployer/Galen.Ci.EntityFramework.Testing/MigrationTestRunner.cs

于 2015-12-17T22:29:10.150 回答
0

我通常采用的方法是(几乎)永远不要对我的数据库架构进行重大更改。它基本上是一种受控形式的技术债务。

例如,假设我将 ColumnX 替换为 ColumnY。典型的做法是“将所有数据从 ColumnX 复制到 ColumnY,从架构中删除 ColumnX”。这会破坏您回滚到以前版本的能力,因为 ColumnX 已经消失了。

解决此问题的回滚友好方法是添加 ColumnY、复制数据并添加触发器以保持两列彼此同步。这不是一个永久的状态!当我们确定我们永远不会回滚到依赖于 ColumnX 的版本时,“删除 ColumnX 和相关触发器”的用户故事会立即出现在积压工作中,以备将来迭代。

回滚仍可能涉及发布 DACPAC 的先前版本,但需要注意的是,您必须确保不会删除数据库中存在的项目而不是模式中的项目。这样,如果您更新了一堆存储过程以从 ColumnY 中提取,您可以发布从 ColumnX 提取的旧版本,而旧版本完全不知道架构已更改。

于 2015-12-15T22:21:52.590 回答