Controlling the flow of migrations in EF Core
Image from Pexels by Ian Beckley

Controlling the flow of migrations in EF Core

Altering EF Core migrations execution order

EF Core has migrations as a way of restoring the database structure inherited from Entity Framework and for the most of the cases all you have to do to setup your database structure changes when deploying your new application version is to call the Migrate method from your DbContext.Database property.

It is not a perfect solution but as I mention, it works great for majority of the cases and takes of the complexity of sorting out order for execution of pending changes so you can focus on business logic of your application.

Note

For this article I will be using some code snippets from the Demo/Sample project written for project https://www.nuget.org/packages/EntityFrameworkCore.SqlServer.Seeding. If you want to check out the whole solution the repository in GitHub is https://github.com/dejanstojanovic/sql-server-script-seeding

Your application migration invocation will probably look something like this

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
    using (var serviceScope = app.ApplicationServices
        .GetRequiredService<IServiceScopeFactory>()
        .CreateScope())
        {
            using (var context = serviceScope.ServiceProvider.GetService<EmployeesDatabaseContext>())
            {
                context.Database.Migrate();
            }
        }
    app.SeedFromScripts();
...
}
    

For most of the cases this just works fine because all the migrations, if created the proper way from the command line are added to the project in order and EF Core takes care of their execution internally so you do not have to interfere.

For some corner cases, you may want to execute something in the middle of the migrations list that are pending for execution and this is what this article is about.

Taking control of migrations execution

For the nuget package EntityFrameworkCore.SqlServer.Seeding project, I initially used similar approach as in EF Core common scenario and that is just to invoke seeding once all migrations are finished.

This way application will ensure that whole infrastructure is in place before the seeding takes place. This makes sense for most of the scenarios and if your application does not need any custom flow alterations this is the way to go.

To keep the pipeline clean, all this is abstracted into an extension method and later just invoked from Configure method in Startup.cs.

        public static void MigrateEmployeesData(this IApplicationBuilder app, IConfiguration configuration)
        {
            using (var serviceScope = app.ApplicationServices
                .GetRequiredService<IServiceScopeFactory>()
                .CreateScope())
            {
                using (var context = serviceScope.ServiceProvider.GetService<EmployeesDatabaseContext>())
                {
                    context.Database.Migrate();
                }
            }
            app.SeedFromScripts();
        }
    

The problems take place when you need to run migrations and seedings interchangeably. For this scenario the code snipped from above won't do because the execution order of EF Core migrations and seeding logic is abstracted into their own single method calls. Now I can always alter my own code for seeding nuget package and release an updated version but for EF Core I need to access the migration service.

Luckily, by design, EF Core internally uses injected instance of interface Microsoft.EntityFrameworkCore.Migrations.IMigrator implementation which takes care of migrations execution. Instead of just calling DbContext.Database.Migrate method, by combining method call DbContext.Database.GetPendingMigrations with methods from IMigrator, we can manually call migrations one by one.

        public static void MigrateAndSeedEmployeesData(this IApplicationBuilder app, IConfiguration configuration)
        {
            using (var serviceScope = app.ApplicationServices
                .GetRequiredService<IServiceScopeFactory>()
                .CreateScope())
            {
                using (var context = serviceScope.ServiceProvider.GetService<EmployeesDatabaseContext>())
                {
                    var migrator = context.Database.GetService<IMigrator>();
                    var migrations = context.Database.GetPendingMigrations();
                    var seeder = serviceScope.ServiceProvider.GetService<ISeeder>();
                    var seeds = seeder.GetPendingScripts();

                    var commands = migrations.Concat(seeds).OrderBy(c => c).ToList();

                    if (commands != null && commands.Any())
                    {
                        foreach (var command in commands)
                        {
                            if (command.EndsWith(".sql"))
                                seeder.ExecuteScript(command);
                            else
                                migrator.Migrate(command);
                        }
                    }
                }
            }
        }
    

Both migrations and seeding scripts prefixes are date and time migration/seeding file is created, so this makes it easy to determine the execution order and I can easily call migration/seeding in mixed flow.

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article