Auto running EF migrations on Azure app service - entity-framework

Our app runs on Azure with read/write permission on the DB. We are not nuts about enabling EF's automatic migrations for a production application, and because the DB user account isn't owner, it doesn't have permission to run migrations anyway.
I have used with a script to run migrate.exe to apply migrations in the past, and was happy with the result. Is there a way to accomplish this on an azure app service?
Currently running the migrations right from visual studio.

One option would be to use a Web Job to run your migration script on a schedule, or on-demand:
Web Job overview
You'd probably want to set up a CI deployment so that the current EF migration script is available to the job:
Web Jobs + CI
And more info
Have a good backup strategy in place for your database, so you can smoke test completed migrations in a staging environment (preferably in an automated fashion) before flipping it into production.
Good luck!

What I ended up doing is making a small console application as a web job, which I set up to be manually triggered. This ensures that my migrator.exe gets built and placed in a place I know.
public static void Main(String[] args)
{
Log("Starting");
var cs = GetConnectionString(args);
if(String.IsNullOrEmpty(cs))
{
Log($"Connnection must be passed as a command argument, or in the environment variable {ConnectionStringVar}, or in the appSetting {ConnectionStringVar} (in that order).");
Log("Exiting without running migrations.");
return;
}
try
{
var c = new Configuration();
c.TargetDatabase = new DbConnectionInfo(cs, "System.Data.SqlClient");
var migrator = new DbMigrator(c);
Log($"{migrator.GetPendingMigrations().Count()} pending migrations");
migrator.Update();
Log("Complete");
}
catch (Exception e)
{
Log($"Failed: {e.GetType().FullName} {e.Message}");
throw;
}
}
Then I generated a custom deployment script: https://github.com/projectkudu/kudu/wiki/Custom-Deployment-Script
In that script, I call my migrate.exe which runs the migrations. Has been working quite well.

Related

Run different database types in different environments in dotnet core 2

I want to run a SQLite database in development and a SQLServer Express database in production.
We are using code first with database migrations.
How do I inject a different dbcontext in each environment?
How do I run migrations against a specific database. E.g. In development I'll want to run migrations against the SQLite database.
So I guess I found a nice way for you to do that. You can use the ConfigureDevelopmentServices startup convention to add your SQLSite DbContext. So, just as some basic example you would have:
// Production "like" ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
// Use Sql Server
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ProductionConnection")));
}
// Development ConfigureServices
public void ConfigureDevelopmentServices(IServiceCollection services)
{
// Use SQL Lite
services.AddDbContext<SchoolContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("DevelopmentConnection")));
}
You can even go further and add a ConfigureStagingServices if you happen to have another different context for staging only. To avoid copy and pasting of common services, you could have a private method that register the common services and have the separate ones only with specific stuff.
Now for the migrations, I never tested this but, my best guess is if you have the correct dbContext and the correct connection string, the migrations will work fine. You just point to the EF project and run it.
For the official MS response, see Use SQLite for development, SQL Server for production

Seamless EF Migrations from Staging > Production with Schema Change

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

Stop a referenced dll from running migrations

I have a solution with a web application. This application uses Entity Framework and Code First. Then I have a second project, a console application. This console application shares the assembly that hold the migrations.
Is there a way to this console application that it should never run the Migrations? It could happen that this console application would contain a newer version which has not yet been deployed in the web application. I like to make sure there is no risk for the console to ruin the web application. Better to just have the console failing that updating the database.
Set the database initialiser to null in your DbContext constructor:
public class ConsoleContext : DbContext
{ public ConsoleContext()
: base("Name=" + Config.ConnectionStringName)
{
// Prevent attempt to initialize a database for this context
Database.SetInitializer<DtoContext>(null);
}
}
Alternatively,
in your web application:
protected void Application_Start()
{
Database.SetInitializer<MyDbContext>(
new MigrateDatabaseToLatestVersion<MyDbContext, Migrations.Configuration>());
}
and in your console application:
protected void Application_Start()
{
Database.SetInitializer<DtoContext>(null);
}
And, to be doubly certain, use two different database users. The web application will need db_datareader, db_datawriter and db_ddladmin roles if you're using Sql Server. The console application should only be in db_datareader and db_datawriter roles to prevent it changing the database schema
Easy) Yes, I have a good solution for that. in Nuget Console you have to provide the connection string and the target migration for the commands: Add-Migration/Update-Database.
Need Code) You can also do that programmatically by using DbMigrator.

Deploying to Azure not running EF Code First Migrations

I am running Entity Framework with Code First Migrations. My new release adds a table, modifies a few tables, and run some scripts. This works perfectly when developing locally using update-database.
Upon deployment the new table was not created and I was receiving errors from my client. I attached the debugger to the deployed site to track through what was happening. It reached the controller, went through the normal flow, and upon hitting the first database call to the new (but not actually existing yet) table it hopped into the Configuration class for my migration.
internal sealed class Configuration : DbMigrationsConfiguration<myProject.api.AuthContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
MigrationsDirectory = #"Migrations\Auth";
ContextKey = "myProject.api.AuthContext";
}
So I'm thinking great, all should be well. It goes through all of these, returns to the initial database call, but then that call returns an error, pasted below
The model backing the 'AuthContext' context has changed since the
database was created. Consider using Code First Migrations to update
the database (http://go.microsoft.com/fwlink/?LinkId=238269).
This is surprising since indeed I have enabled code first migrations! Using the standard enable-migrations and add-migration, which work perfectly on my local machine when I issue the update-database command.
1) How can I get my Code First migrations to run once deployed to Azure?
Update 1
Here are my publish settings. For some reason I do not have the checkbox option for: "Execute Code First Migrations" and am guessing that's the issue...
Huzzah! My update was the issue. I found from other SO posts that his happens sometimes. A Clean and Rebuild, followed by restarting VS, restored the 'Execute Code First Migrations' checkbox. Re-deployed and everything worked perfectly.
Have you checked these lines of code?
In your web.config:
<appSettings>
<add key="MigrateDatabaseToLatestVersion" value="true"/>
</appSettings>
In your global.asax.cs / Startup.cs (OWIN startup):
var configuration = new Migrations.Configuration();
var migrator = new DbMigrator(configuration);
migrator.Update();

Entity Framework Migrations - Seed runs even if no migration?

I have set up a simple Migration with AutomaticMigrationsEnabled = false. Everything works great from visual studio or when using MigrateDatabaseToLatestVersion.
However, this is not ideal for me. I would like to run migrations from a deployment script on my ci server. I found this article explaining how to do this using migrate.exe but this seems to always run the seed. This is even when there are no migrations to apply.
Do I need to check programmatically within the Seed method whether any migrations have been run? How do I do this?
Use DbMigrator to manually run Update() only if there are pending migrations. It was introduced in Entity Framework 5.0.
private void MigrateAndSeedDbIfSchemaIsOutdated()
{
// Disable initializer.
Database.SetInitializer<MyContext>(null);
// Make sure database exists.
using (var db = new MyContext())
{
db.Database.Initialize(false);
}
var migrator = new DbMigrator(new MyConfiguration());
if (migrator.GetPendingMigrations().Any())
{
// Run migrations and seed.
migrator.Update();
}
}