EF Code First 迁移以部署旧版本 [英] EF Code First Migrations to Deploy Older Version

本文介绍了EF Code First 迁移以部署旧版本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 TFS Release Management 进行持续集成和部署.

I'm using TFS Release Management to do continuous integration and deployment.

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

I'm using migrate.exe to perform the database migration during deployment, and this works great when you're going from an older version to a newer version. When you want to deploy an older version of the application, however, it gets more muddy.

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

Basically, the assembly that holds your migrations for a context must know how to go from say version 3 to version 2. Normally, you use the assemblies you're about to deploy as the source of your migrations, but in this case, you must use the already deployed assemblies as they're the only ones who know how to get from v3 down to v2. (Version 2 has no idea that v3 even exists.)

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

My current plan is to somehow compare the two assemblies during deployment. If the assembly in the installation directory contains "newer" migrations than the one in the deployment director, I would first need to get the "newest" available migration in the assembly in the deployment directory, and then execute:

migrate.exe AssemblyInInstallationDir /targetMigration NewestFromAssemblyInDeploymentDir

在升级到较新版本的正常"部署场景中,您只需执行以下操作:

Where as in a "normal" deployment scenario where you are upgrading to a newer version, you can just do:

migrate.exe AssemblyInDeploymentDir

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

Is this a legit approach? I have yet to look into using EF libraries to evaluate what migrations are available in each assembly. There is also the challenge of the fact that each of these assemblies are the "same" just different versions. I'll probably have to load them into separate app domains and then use cross-app domain communications to get the information I need.

编辑

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

I created a proof of concept app that allows me to list the available migrations to two different versions of the same assembly. This was critical to this entire process, so I figured it's worth documenting.

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

The app uses reflection to load each of the assemblies and then uses the DbMigrator class from System.Data.Entity.Migrations to enumerate the migration meta data. The names of the migrations are prefixed with the timestamp information, thereby allowing me to order them and see which assembly contains the "newer" set of migrations.

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("	{0}", m);
    }

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

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

    Console.ReadKey();
}

}

应用程序的输出看起来像:

The output of the application looks like:

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

推荐答案

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

We actually solved this problem and have been using our tooling for over a year now to do fully continuous database deployments into production. No humans involved. :)

我们已在 GitHub 上公开了部分内容:https://github.com/GalenHealthcare/Galen.Ef.Deployer

We've made some of this public on GitHub: https://github.com/GalenHealthcare/Galen.Ef.Deployer

您可以进行重大"更改,但一般来说,我们也会避免这种情况 - 但主要是因为我们的应用程序在升级过程中仍然有效.我们将数据层视为可独立部署的组件——因此,它有一个需要保持兼容的接口".

You can make "breaking" changes, but generally, we avoid that as well - but mostly because our applications remain live during upgrades. We treat the data tier as an independently deployable component - and as a result, it has an "interface" that needs to remain compatible.

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

We will often use a multi-phased upgrade approach where we deploy an intermediate version that's backwards/forwards compatible, upgrade our various application services, and then finally upgrade the database tier to remove the legacy compatibility.

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

Even in that scenario, we have the ability to automatically go from/to any version of our schema and data. In fact, we've added unit tests that verify this every single time we build for every single database version. It basically walks up/down the chain of schema iterations and validates that the upward and downward migrations always work and maintain data consistency and compatibility. You can see these tests in the GitHub project. Here is an example:

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

这篇关于EF Code First 迁移以部署旧版本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆