Alica's dev blog
Database versioning with DbUp and SSDT - Part 2: Set up DbUp

In the previous article Part 1: Intro, I wrote about approaches to database versioning and why I chose the combination of SSDT project and DbUp library.

Now, we will get DbUp up and running with an ASP.NET Core application.

There is some documentation, but it is not very detailed. I had to read through source code a couple of times to find the pieces of information that I needed. Therefore we will to spend some time on the setup to help you do it quickly.

This setup assumes that the database will be upgraded when the application starts. That is very convenient for development but there are couple of reasons why you probably shouldn’t do it in production:

  • Your production server might not (or even should not) have the necessary database permissions.
  • The migration process can take a long time, depending on the size of the database.

The solution would be to create a separate application that performs the migration. I have to admit that I haven’t done it yet but it doesn’t look like a difficult task.

So let’s do it!

Create the upgrade method

First, we create the upgrade method that actually calls DbUp.

We need to tell it:

  1. How to connect to the database. There isn’t an option to use an existing connection, you have to provide the connection string.
  2. Where to find migration files. There are multiple options (not well documented, I recommend looking into the source code for further clarification). In our case, we want to specify a folder containing the scripts (I placed them in the SSDT project).
  3. Where to log. More on that in the next section.
public void UpgradeDatabase(string connectionString, string scriptsFolder, ILogger logger)
{
    var dbUpgrader = DeployChanges.To
        .SqlDatabase(connectionString)
        .WithScriptsFromFileSystem(scriptsFolder)
        .LogTo(new DbUpLogger(logger))
        .Build();

    var result = dbUpgrader.PerformUpgrade();

    if (!result.Successful)
        throw result.Error;
}

Configure logging

The LogTo method is a bit misbehaved and doesn’t accept ILogger as an input. Instead, it requires the logger to implement DbUp’s own IUpgradeLog interface.

Fortunately, we can easily solve that by creating our own DbUpLogger that simply wraps ILogger:

public class DbUpLogger : IUpgradeLog
{
    private readonly ILogger logger;

    public DbUpLogger(ILogger logger)
    {
        this.logger = logger;
    }

    public void WriteInformation(string format, params object[] args)
    {
        logger.LogInformation(format, args);
    }

    public void WriteError(string format, params object[] args)
    {
        logger.LogError(format, args);
    }

    public void WriteWarning(string format, params object[] args)
    {
        logger.LogWarning(format, args);
    }
}

Do the migration

We create an extension method for IHost where we gather values for all parameters. Then we call the UpgradeDatabase method that we created in the previous section.

public static IHost MigrateDatabase(this IHost host)
{
    // we need the scope to obtain instances of IConfiguration and ILogger
    using (var scope = host.Services.CreateScope())
    {
        var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
        
        var scriptsFolder = configuration.GetSection("DbUp").GetValue<string>("ScriptsFolder");

        var connectionString = configuration.GetConnectionString("Default");
        
        var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();

        try
        {
            UpgradeDatabase(connectionString, scriptsFolder, logger);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "The database couldn't be upgraded.");
            throw;
        }   
    }

    return host;
}

The last step is to call the MigrateDatabase method from Main:

public static void Main(string[] args)
{
    CreateHostBuilder(args)
        .Build()
        .MigrateDatabase()
        .Run();
}

And now your database will be upgraded every time you run the application.

What’s next

Read the next posts in the series:

The previous one was:


Last modified on 2021-04-09