DbUp wants to run previously run scripts

611 Views Asked by At
             IConfiguration config = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .AddJsonFile("appsettings.Development.json", true)
                .AddEnvironmentVariables() // Overwrite with any environment variables, e.g., from Azure environments.
                .Build();

           var upgrader =
                DeployChanges.To
                    .SqlDatabase(connectionString)
                    .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => !s.Contains(TestDataScriptsFolderName), 
                    new DbUp.Engine.SqlScriptOptions
                    {
                        ScriptType = DbUp.Support.ScriptType.RunOnce,
                        RunGroupOrder = 1
                    })
                    .LogToConsole();

            if (args.Any(a => a.Contains("IncludeTestData")) || config.GetValue<bool>("IncludeTestData"))
            {
                Console.WriteLine("Test data scripts will be executed as part of the upgrade.");

                upgrader.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains(TestDataScriptsFolderName),
                    new DbUp.Engine.SqlScriptOptions
                    {
                        //ScriptType = DbUp.Support.ScriptType.RunAlways, 
                        RunGroupOrder = 2
                    })
                    .LogToConsole();
            }

            DbUp.Engine.UpgradeEngine u = upgrader.Build();

            List<DbUp.Engine.SqlScript> allScripts = u.GetDiscoveredScripts();
            // 52 -- all of them

            List<string> executedScripts = u.GetExecutedScripts();
            // 543 -- because the test data scripts (which are idempotent) were run at every deployment.

            List<DbUp.Engine.SqlScript> todoScripts = u.GetScriptsToExecute();
            // All 52! It should be 0 because all scripts have been run.

            bool ug = u.IsUpgradeRequired();

            Console.WriteLine(allScripts.Any(z => z.Name.EndsWith("T20200518T1023_InitialTestData.sql")));
            Console.WriteLine(executedScripts.Any(z => z.EndsWith("T20200518T1023_InitialTestData.sql")));
            Console.WriteLine(todoScripts.Any(z => z.Name.EndsWith("T20200518T1023_InitialTestData.sql")));

            var result = upgrader.Build().PerformUpgrade();

As above, GetScriptsToExecute should return an empty array because all scripts have been run -- and are listed in the SchemaVersions table. But it's returning all scripts. Why?

2

There are 2 best solutions below

1
On

The problem was that the namespace of the csproj had changed and it is prepended to the script names in the SchemaVersions table which threfore did not match to the script names in the renamed project.

0
On

I created a custom comparer for script names.

public class IgnorePrefixesComparer : IComparer<String>
{
    private readonly (string, string) _prefixes;

    public IgnorePrefixesComparer((string,string) prefixes)
    {
        _prefixes = prefixes;
    }
    public int Compare(string? a, string? b)
    {
        if (a is null || b is null) return StringComparer.OrdinalIgnoreCase.Compare(a, b);

        RemovePrefix(ref a);
        RemovePrefix(ref b);
        return StringComparer.OrdinalIgnoreCase.Compare(a, b);
    }

    private void RemovePrefix(ref string scriptName)
    {
        if (scriptName.StartsWithIgnoreCase(_prefixes.Item1)) scriptName = scriptName.Substring(_prefixes.Item1.Length);
        if (scriptName.StartsWithIgnoreCase(_prefixes.Item2)) scriptName = scriptName.Substring(_prefixes.Item2.Length);
    }
}

It's used like this:

var builder = DeployChanges.To
   .SqlDatabase(connectionString)
   .WithScriptsEmbeddedInAssembly(assemblyWithScripts)
   .LogToAutodetectedLog();

builder.Configure(c =>
{
    var customComparer = new IgnorePrefixesComparer(("OldPrefix", "NewPrefix"));
    c.ScriptNameComparer = new ScriptNameComparer(customComparer);
});

var result = builder.Build().PerformUpgrade();