从分期进行无缝EF迁移>生产与模式变化 [英] Seamless EF Migrations from Staging > Production with Schema Change
问题描述
- 将App部署到分段
- 交换分段<>生产插槽
当我有一个DB迁移来处理时,这会变得更加棘手。现在我所做的是:
- 将应用程序部署到分段
- 部署准备就绪后,运行更新数据库到Prod(无分段数据库)
- 交换分段<>生产插槽
这意味着我仍然有效地停机,因为2 + 3不会同时发生,这意味着在几秒钟之内,我的用户会遇到DB模式已经改变的不完美的行为。
这里最简单的解决方案是什么?我想我可能也需要启动一个分期数据库,但是我不得不担心复制和连接字符串管理会增加一些开销。
当我们将解决方案转移到持续交付模式时,我们遇到了同样的困境,并希望避免停机。
您需要将您的EF配置为在开发环境中运行 Code-First ,并在生产中运行 Database-First 。
这样可以推送更改生活在三个阶段:
阶段1.数据库迁移
在这个阶段,您将使用EF migrate.exe
实用程序(或者简单地在手上编写脚本),以便根据实时数据库运行最新的迁移。迁移后,您的网站在生产中仍然保持功能,因为没有发生任何事情(因为它被配置为首先是数据库)。
重要的一点是,您需要确保在此阶段的迁移是添加,在某种意义上它会改变一个让我们说会使现场网站崩溃的表或列。它可能看起来很可怕,但如果您的项目已经足够成熟,您很快将意识到,您的模式的大部分更改是完全添加的,或者可以分为两个阶段。 (见阶段3)
阶段2.更新制作网站
在这个阶段,你的普通
阶段3.数据库迁移(第2部分)
在这样一些罕见的情况下,例如数据库表或列被重命名,您将需要考虑将其分为两个步骤:
- 添加新列(在第1部分中完成)
- 删除旧列并迁移数据(在第2部分中完成)。
附录
EF数据库 - 仅在生产中首先
在您的 Startup.cs
或 Global.asax.cs
:
#if DEBUG
Database.SetInitializer(新的MigrateDatabaseToLatestVersion&AppDatabase,Migrations.Migrations.Configuration>());
#else
Database.SetInitializer(新的RequireDatabaseToBeUpToDate&AppDatabase,Migrations.Migrations.Configuration>());
#endif
这完全符合锡上所说的:
- 本地:将数据库迁移到最新的迁移。
- :确保数据库迁移是正在使用的模型程序集的 NOT AHEAD 。 - 这是一个安全措施,确保即使我们在数据库之前意外部署了网站,也阻止了网站的启动。
public class RequireDatabaseToBeUpToDate< TContext,TMigrationsConfiguration> :IDatabaseInitializer< TContext>
其中TContext:DbContext
其中TMigrationsConfiguration:DbMigrationsConfiguration,new()
{
public void InitializeDatabase(TContext context)
{
var migrator = new DbMigrator (新的TMigrationsConfiguration());
var migrations = migrator.GetPendingMigrations()。ToList();
if(migrations.Any())
{
var message =在启动应用程序之前,有待处理的迁移必须应用(通过脚本或使用migrate.exe)。 \\\\
+
$待迁移:\r\\\
{string.Join(\r\\\
,migrations)};
throw new MigrationsPendingException(message);
}
}
}
对活动数据库运行迁移
$ migrate =< path> \migrate.exe
$ migrateConfig =< path> \\ migrate.exe.config
$ connectionString =< your-live-connection-string>
& $ migrate< your-project-migration-assembly> / startupConfigurationFile = $ migrateConfig< your-migration-configuration-type-name> / connectionString = $ connectionString /connectionProviderName=System.Data.SqlClient / verbose
I have a simple web app. It consists of an Azure Web App with Staging and Production slots. When there are no DB migrations to consider, I can easily achieve a seamless update by:
- Deploy App to Staging
- Swap Staging <> Production Slots
This gets trickier when I have a DB migration to handle. Right now what I do is:
- Deploy App to Staging
- When deployment is ready, run update-database to Prod (no staging database)
- Swap Staging <> Production Slots
This means that I still effectively have downtime as 2 + 3 don't happen simultaneously, which means that for some seconds, my users will experience imperfect behavior as the 'DB schema has changed'.
What is the simplest solution here? I'm thinking I may have to spin up a staging database too, but then I have to worry about replication and connection string management which adds a bit of overhead.
We had the same dilemma when moving our solution to continuous delivery model and wanted to avoid downtime.
You need to configure your EF to run Code-First on development environment and Database-First in production.
This makes it possible to push your changes to live in three stages:
Stage 1. Database Migrations
At this stage, you will use EF's migrate.exe
utility (or simply script them before hand) to run your latest migrations against the live database. After migrations are applied your website in production still keeps functioning as nothing has happened (because it's configured to be database-first).
The important bit is that you need to make sure your migrations at this stage are additive, in the sense that it would'd change a let's say table or column that will cause the live site to crash. It may look scary, but if your project is mature enough, you soon will realise that most of changes to your schema are either completely additive or can be broken down into two stages. (see stage 3)
Stage 2. Update production website
At this stage do your normal Staging --> Production website deployment.
Stage 3. Database Migrations (part 2)
In those rare cases where you had for example a database table or column renamed, you will need to consider breaking it into two steps of:
- Add a new column (done in part 1)
- Remove old column and migrate data (done in part 2).
Appendices
EF Database-First only in production
In your Startup.cs
or Global.asax.cs
:
#if DEBUG
Database.SetInitializer(new MigrateDatabaseToLatestVersion<AppDatabase, Migrations.Migrations.Configuration>());
#else
Database.SetInitializer(new RequireDatabaseToBeUpToDate<AppDatabase, Migrations.Migrations.Configuration>());
#endif
This does exactly what it says on the tin:
- On Local: Migrates it's database to latest migration.
- In Production: Ensures that the database migrations is NOT AHEAD of the model assembly it is using. -- this is a safety measure making sure even if we ever accidentally deployed web before database, it stops the site from firing up.
public class RequireDatabaseToBeUpToDate<TContext, TMigrationsConfiguration> : IDatabaseInitializer<TContext>
where TContext : DbContext
where TMigrationsConfiguration : DbMigrationsConfiguration, new()
{
public void InitializeDatabase(TContext context)
{
var migrator = new DbMigrator(new TMigrationsConfiguration());
var migrations = migrator.GetPendingMigrations().ToList();
if (migrations.Any())
{
var message = "There are pending migrations that must be applied (via a script or using migrate.exe) before the application is started.\r\n" +
$"Pending migrations:\r\n{string.Join("\r\n", migrations)}";
throw new MigrationsPendingException(message);
}
}
}
Running migrations against live database
$migrate = "<path>\migrate.exe"
$migrateConfig = "<path>\migrate.exe.config"
$connectionString = <your-live-connection-string>
& $migrate <your-project-migration-assembly> /startupConfigurationFile=$migrateConfig <your-migration-configuration-type-name> /connectionString=$connectionString /connectionProviderName=System.Data.SqlClient /verbose
这篇关于从分期进行无缝EF迁移>生产与模式变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!