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:
- Install the FluentMigrator NuGet package
- Install the FluentMigrator.Runner NuGet Package
- Install the Dapper.NET NuGet Package
- Change the assembly name mentioned on
private static Assembly migrationsAssembly = Assembly.Load("...")
to the name of the assembly with your migrations classes. - Add a
using Dapper
statement to the top
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.