Running FluentMigrator Migrations at Runtime

FluentMigrator’s Migrations Runners wiki page doesn’t mention any way of running migrations through code.

Why might you want to do this? Because your application is in the hands of users, or ops teams, or someone who needs to run the migrations. Or, because you want to auto-run migrations, say, on start-up.

It’s not a trivial amount of work, but it’s achievable. The core logic is in this MigrationsWrapper class I wrote:

public class MigrationsWrapper
    {
        private static Assembly migrationsAssembly = Assembly.Load("...");

        private string connectionString;
        private Action<string> logMethod = Console.WriteLine;

        public MigrationsWrapper(string connectionString, Action<string> logMethod = null)
        {
            this.connectionString = connectionString;
            if (logMethod != null)
            {
                this.logMethod = logMethod;
            }
        }

        private MigrationRunner GetMigrator()
        {
            var announcer = new TextWriterAnnouncer(s => logMethod.Invoke(s));

            var migrationContext = new RunnerContext(announcer);
            var options = new MigrationOptions { PreviewOnly = false, Timeout = 60 };
            var factory = new FluentMigrator.Runner.Processors.SqlServer.SqlServer2008ProcessorFactory();
            var processor = factory.Create(this.connectionString, announcer, options);
            var runner = new MigrationRunner(migrationsAssembly, migrationContext, processor);

            return runner;
        }

        public void MigrateToLatestVersion()
        {
            var runner = GetMigrator();
            runner.MigrateUp(LatestVersionNumber);
        }

        public void MigrateToVersion(int version)
        {
            var runner = GetMigrator();

            long currentVersion = CurrentVersionNumber;

            if (currentVersion < version)
            {
                runner.MigrateUp(version);
            }
            else if (currentVersion > version)
            {
                runner.MigrateDown(version);
            }
        }

        public long LatestVersionNumber
        {
            get
            {
                long toReturn = 0;
                // Look through all types
                foreach (Type t in migrationsAssembly.GetTypes())
                {
                    // Get all the types with MigrationAttribute (object[] because it can have multiple Migration attributes)
                    object[] attributes = t.GetCustomAttributes(typeof(MigrationAttribute), true);
                    if (attributes.Length > 0)
                    {
                        // Get the max of (current max, max version specified in this Type's Migration attributes)
                        toReturn = Math.Max(toReturn, attributes.Max(o => (o as MigrationAttribute).Version));
                    }
                }

                return toReturn;
            }
        }

        public long CurrentVersionNumber
        {
            get
            {
                long toReturn = 0;

                using (var conn = new SqlConnection(this.connectionString))
                {
                    conn.Open();

                    try
                    {
                        toReturn = conn.Query<long>("SELECT MAX(Version) FROM VersionInfo").First();
                    }
                    catch (SqlException)
                    {
                        toReturn = 0;
                    }
                }
                return toReturn;
            }
        }

        private class MigrationOptions : IMigrationProcessorOptions
        {
            public bool PreviewOnly { get; set; }
            public string ProviderSwitches { get; set; }
            public int Timeout { get; set; }
        }
    }

To run it, you just call new MigrationsWrapper(connectionString).MigrateToLatestVersion() and you’re done.

By default, it prints out messages to Console.WriteLine. Instead, you can pass in whatever Action<string> method you want.

To get this to compile in your project:

Resolve compilation errors with CTRL + . until everything compiles, and you’re done.

The core is the creation of a new MigrationRunner instance (from the FluentMigator.Runners package, and creating all required objects for settings.

MigrationsWrapper also uses some reflection trickery to get the latest version number.

Also, make sure your migrations classes are public. If they’re not, the migrations runner won’t find and run them.

About Ashiq Alibhai, PMP

Ashiq has been coding C# since 2005. A desktop, web, and RIA application developer, he's touched ASP.NET MVC, ActiveRecord, Silverlight, NUnit, and all kinds of exciting .NET technologies. He started C# City in order to accelerate his .NET learning.
This entry was posted in Libraries, Web, Wndows Forms and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *