Using DbUp

Using DbUp
Photo by Markus Spiske: https://www.pexels.com/photo/codes-on-tilt-shift-lens-2004161/

On a recent project there wasn't an agreed way to run DB scripts. So we built a small command line tool using DbUp. We then wrote the scripts and had them as embedded files within the project - DbUp could then access the scripts and run any that needed to be applied.

We also used CommandLine to give us a nice help screen and the ability to specify parameters.

Uses variables to inject values into scripts.

using System.Reflection;
using CommandLine;
using DbUp;
using DbUp.Engine;
using DbUp.Support;

Parser.Default.ParseArguments<Options>(args).WithParsed(o =>
{
    Console.WriteLine($"Deploying to : {o.ConnectionString}");

    try
    {
        EnsureDatabase.For.SqlDatabase(o.ConnectionString);

        var executingAssembly = Assembly.GetExecutingAssembly();
        var result = DeployChanges.To
            .SqlDatabase(o.ConnectionString)
            .WithScriptsEmbeddedInAssembly(executingAssembly, IsPreDeployment, new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 0 })
            .WithScriptsEmbeddedInAssembly(executingAssembly, IsScript, new SqlScriptOptions { RunGroupOrder = 1 })
            .WithScriptsEmbeddedInAssembly(executingAssembly, IsReferenceData, new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 2 })
            .WithScriptsEmbeddedInAssembly(executingAssembly, IsFunction, new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 3 })
            .WithScriptsEmbeddedInAssembly(executingAssembly, IsStoredProcedure, new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 4 })
            .WithScriptsEmbeddedInAssembly(executingAssembly, IsView, new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 5 })
            .WithScriptsEmbeddedInAssembly(executingAssembly, IsPostDeployment, new SqlScriptOptions { ScriptType = ScriptType.RunAlways, RunGroupOrder = 10 })
            .LogToConsole()
            .LogScriptOutput()
            .WithTransaction()
            .WithVariable("password", o.Password)
            .WithVariable("login", o.Login)
            .WithVariable("user", o.User ?? o.Login)            
            .Build()
            .PerformUpgrade();

        if (result.Successful)
        {
            Environment.Exit(0);
            return;
        }

        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(result.Error);
        Console.ResetColor();

        Environment.Exit(1);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
        Environment.Exit(2);
        throw;
    }
});

static bool IsPreDeployment(string scriptName) => ScriptNameStartsWith(scriptName, "PreDeployment.");
static bool IsPostDeployment(string scriptName) => ScriptNameStartsWith(scriptName, "PostDeployment.");
static bool IsScript(string scriptName) => ScriptNameStartsWith(scriptName, "Scripts.");
static bool IsReferenceData(string scriptName) => ScriptNameStartsWith(scriptName, "ReferenceData.");
static bool IsFunction(string scriptName) => ScriptNameStartsWith(scriptName, "Functions.");
static bool IsView(string scriptName) => ScriptNameStartsWith(scriptName, "Views.");
static bool IsStoredProcedure(string scriptName) => ScriptNameStartsWith(scriptName, "StoredProcedures.");
static bool ScriptNameStartsWith(string scriptName, string startsWith) => scriptName.StartsWith($"{Assembly.GetExecutingAssembly().GetName().Name}.{startsWith}", StringComparison.OrdinalIgnoreCase);

internal class Options
{
    [Option('c', "connectionString", Required = true, HelpText = "SQL server connection string")]
    public string? ConnectionString { get; set; }

    [Option('p', "password", Required = true, HelpText = "SQL server user password")]
    public string? Password { get; set; }

    [Option('l', "login", Required = true, HelpText = "SQL server login")]
    public string? Login { get; set; }

    [Option('u', "user", Required = false, HelpText = "SQL server database user")]
    public string? User { get; set; }
}