问题
After reading many questions here, on Asp.Net's Github (this one comes to mind, ServerFault and SoftwareEngineering, I have decided to ask a question of my own.
The Problem
Code-First Migrations make live incredibly easy during early development. Staging- or Production-Environments are harder to update, especially during the initial roll-outs which may require frequent updates. Updating the production DB is a lot harder, and automatic migrations are out of the question for a number of very good reasons - which are all nicely written and explained in the documentation and made me ignore that route. Now I'm looking for a middle ground.
Approach
Once a new version of the application is published, we could detect if there are pending changes (The diagnostics middleware "DatabaseErrorPageMiddleware" does this already when we use it, which obviously we wouldn't do in a production environment). If there are pending migrations, we could take the site offline (there are various methods, skipped for brevity), but still allow an administrative user to login to the system - maybe one with the particular claim "IsAllowedToMigrate":"true". For this user, we could add an action to his administrative navigation. That page could list the pending changes, and expose methods to update to current (for simplicity). This action method could even incorporate code to backup the database or similar checks and balances.
Assumptions about the environment
For my intended approach, I'm assuming a rather informal approach to updating an application - no external DBA involved who may require scripts, migrations have already been tested on a very recent backup of the production database etc.
Secondly, I'm assuming that the Site can be displayed as offline; let's make a call to ImaginarySiteManager.DisplayOffline(AVeryNiceMessage)
. That imaginary site could still offer a login to an administration backend.
I'm also tailoring this to SQL Server, and will remain ignorant about other providers.
Some Code
Most of this code has shamelessly been stolen from the source of the Middleware, to test it I've just used an ActionMethod available to the Administrator, no output or actions are attached yet
public async Task<IActionResult> TestPendingMigrations([FromServices] SomeDbContext dbContext)
{
var relationalDatabaseCreator = dbContext.GetService<IDatabaseCreator>() as IRelationalDatabaseCreator;
var migrationsAssembly = dbContext.GetService<IMigrationsAssembly>();
var modelDiffer = dbContext.GetService<IMigrationsModelDiffer>();
var databaseExists = await relationalDatabaseCreator.ExistsAsync();
// HasDifferences will return true if there is no model snapshot, but if there is an existing database
// and no model snapshot then we don't want to show the error page since they are most likely targeting
// and existing database and have just misconfigured their model
var pendingModelChanges
= (!databaseExists || migrationsAssembly.ModelSnapshot != null)
&& modelDiffer.HasDifferences(migrationsAssembly.ModelSnapshot?.Model, dbContext.Model);
var pendingMigrations
= (databaseExists
? await dbContext.Database.GetPendingMigrationsAsync()
: dbContext.Database.GetMigrations())
.ToArray();
// get Sitemanager.CurrentSite -> isClosed; closedMessage = "updates pending.." or similar.
// add action to admin menu (update database).
return new EmptyResult();
}
stuck here
First of all, the test whether pending migrations exist should sit somewhere in or just off Startup. I'm trying to figure out the best place to set this up.
DbContext.OnModelCreating
looks like a good place to start?
But how do I wait for that to be complete and plug in my SherlockHolmes module to investigate?
The code to apply a migration is also written, in the sixth book of Moses: https://github.com/aspnet/AspNetCore/blob/master/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.cshtml
So, in theory it should be possible to
- Start the (updated or not) application
- Startup (or derivates) figure out if the database matches the model (
pendingMigrations
) and take the site offline - an administrator with a particular permission (claim or otherwise) could login, and run Migrations right from the backend - as the sole authorised person to do it if it had to happen
Is this a viable approach? Did I miss the Microsoft.EntityFramework.Extensions.ProductionMigrationMadeSimple
Extension and overthink this?
来源:https://stackoverflow.com/questions/54259010/net-core-entity-framework-core-migrations-production