Database Migrations and Seeding in ASP.NET Core

Tuesday, September 20, 2016

There is an instant in time when an ASP.NET application is fully alive and configured but it still held in check and waiting for a signal from the starter’s gun. This moment exists between the lines of code in Program.cs, and it is here where I’ve found a nice place to automatically run database migrations and seed a database based on command line arguments to the program.

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

    ProcessDbCommands(args, host);

    host.Run();
}

ProcessDBCommands is the method I use in the above code, and the logic here can be as simple or as complicated as you need. In my case, I’m just going to look for keywords in the arguments to drop, migrate, and seed the database. For example, running “dotnet run dropdb migratedb seeddb” will execute all three options against the configured database.

private static void ProcessDbCommands(string[] args, IWebHost host)
{
    var services = (IServiceScopeFactory)host.Services.GetService(typeof(IServiceScopeFactory));

    using (var scope = services.CreateScope())
    {
        if (args.Contains("dropdb"))
        {
            Console.WriteLine("Dropping database");
            var db = GetLeagueDb(scope);
            db.Database.EnsureDeleted();
        }
        if (args.Contains("migratedb"))
        {
            Console.WriteLine("Migrating database");
            var db = GetLeagueDb(scope);
            db.Database.Migrate();
        }
        if (args.Contains("seeddb"))
        {
            Console.WriteLine("Seeding database");
            var db = GetLeagueDb(scope);
            db.Seed();
        }
    }        
}

private static LeagueDb GetLeagueDb(IServiceScope services)
{
    var db = services.ServiceProvider.GetRequiredService<LeagueDb>();           
    return db;
}

A couple notes on the above code.

IWebHost gives us access to a fully configured environment, so connection strings and services are available just as they are inside the rest of the post-startup application code.

The db.Database.EnsureDeleted and db.Database.Migrate methods are built-in APIs for EF Core. The Seed method, on the other hand, is a custom extension method.


Comments
gravatar Calabonga Tuesday, September 20, 2016
This is very interesting idea! Thanks!
gravatar Kristian Hellang Wednesday, September 21, 2016
I'm not sure if this is a problem or not, but since LeagueDb probably is registered as "scoped" and implements IDisposable (I assume it derives from DbContext), it'll be tracked for disposal when the container is disposed. Since you're resolving it form the application container, it'll end up as a singleton and won't be ever be disposed. I think you should resolve IServiceScopeFactory, get a scope from that and resolve LeagueDb from its ServiceProvider instead, so you make sure it's not keeping a connection to the database or something...
gravatar Scott Wednesday, September 21, 2016
@Kristian: Thanks, good point. Will update code.
gravatar Shahriar Hossain Thursday, September 22, 2016
Informative and helpful (Y)
Comments are closed.

My Pluralsight Courses

K.Scott Allen OdeToCode by K. Scott Allen
What JavaScript Developers Should Know About ECMAScript 2015
The Podcast!