Fluentmigrator: How to use custom version table meta data for new .net core FluentMigrator

Created on 9 Oct 2018  路  7Comments  路  Source: fluentmigrator/fluentmigrator

I am migrating a pre .net core FluentMIgrator project to the new .net core version. I had a custom VersionTable that looked like this:

    [VersionTableMetaData]
    public class VersionTable : DefaultVersionTableMetaData
    {
        public override string TableName => "T_SCHEMA_VERSION_INFO";
    }

This results in the following warning/error:

VersionTable.cs(9,5,18,6): error CS0618: 'DefaultVersionTableMetaData.DefaultVersionTableMetaData()' is obsolete: 'Use dependency injection'

I fixed this error by changing my code to this (removed attribute & added constructor):

    public class VersionTable : DefaultVersionTableMetaData
    {
        public VersionTable(IConventionSet conventionSet, IOptions<RunnerOptions> runnerOptions)
            : base(conventionSet, runnerOptions)
        {
            SchemaName = "distribution";
        }

        public override string TableName => "T_SCHEMA_VERSION_INFO";
    }

This compiles correctly. But then, I don't know what I have to add in my CreateServices function:

            return new ServiceCollection()
                .AddFluentMigratorCore()
                .ConfigureRunner(rb => rb
                    .AddMySql5()
                    .WithVersionTable(new VersionTable(???))

So the WithVersionTable expects an instance of IVersionTableMetaData. Unfortunately, I don't know what to use for the convensionset and runnerOptions parameters.

Could you please provide some guidance and document them here? I have the feeling that this documentation is out of date.

documentation improvement question

Most helpful comment

Or, IMHO, a more elegant option would be to add a .WithVersionTable<VersionTable>() function. The implementation of this function would be something simple like:

        public static IMigrationRunnerBuilder WithVersionTable<T>(
            this IMigrationRunnerBuilder builder) where T:IVersionTableMetaData
        {
            builder.Services
                .AddScoped<IVersionTableMetaDataAccessor, T>();
            return builder;
        }

All 7 comments

You have two options:

  1. Adding the service directly (see example 1)
  2. Implement the IVersionTableMetaData directly (see example 2)

The reason why example 2 works (and doesn't require a custom constructor) is because you're explicitly specifying a schema. The default version table must use the convention set to apply it.

I'll leave this issue open as a reminder to update the documentation.

Example 1

services.AddScoped<IVersionTableMetaData, VersionTable>()

Example 2

    [VersionTableMetaData]
    public class VersionTable : IVersionTableMetaData
    {
        public string ColumnName => "Version";
        public string SchemaName => "distribution";
        public string TableName => "T_SCHEMA_VERSION_INFO";
        public string UniqueIndexName => "UC_Version";
        public string AppliedOnColumnName => "AppliedOn";
        public string DescriptionColumnName => "Description";
        public object ApplicationContext { get; set; }
    }

EDIT: Fixed examples

In Example 1, you still require an instance of versionTablemetaData to be instantiated. Therefore, it doesn't really work since my VersionTable constructor requires parameters.

In Example 2, you are not implementing the ApplicationContext property: https://github.com/fluentmigrator/fluentmigrator/blob/1b2779258a9eec466e8c5d00173f22e731314fb6/src/FluentMigrator.Runner.Core/VersionTableInfo/IVersionTableMetaData.cs#L34

Here's what I used instead:

services.AddScoped<IVersionTableMetaData, VersionTable>()

Would it be an option to simply remove the constructor parameters of DefaultVersionTableMetaData? This would solve all problems related to the instantiation of my VersionTable class. It would also make it possible to easily use the WithVersionTable method.

Yes, you're right. Registering it the way you did is correct. I fixed the examples.

I'll think about removing the constructor without requiring to apply the convention set at every place where the version table metadata is required.

Or, IMHO, a more elegant option would be to add a .WithVersionTable<VersionTable>() function. The implementation of this function would be something simple like:

        public static IMigrationRunnerBuilder WithVersionTable<T>(
            this IMigrationRunnerBuilder builder) where T:IVersionTableMetaData
        {
            builder.Services
                .AddScoped<IVersionTableMetaDataAccessor, T>();
            return builder;
        }

It seems the resources here are not 100% correct. at least with net5

here is my complete working example

internal class VersionTableAccessor : IVersionTableMetaDataAccessor
{
    public IVersionTableMetaData VersionTableMetaData { get; } = new VersionTable();
}

internal class VersionTable : IVersionTableMetaData
{
    public object ApplicationContext { get; set; }
    public string AppliedOnColumnName => "APPLIED_ON";
    public string ColumnName => "VERSION";
    public string DescriptionColumnName => "MIGRATION";
    public bool OwnsSchema => true;
    public string SchemaName => "MIG";
    public string TableName => "HISTORY";
    public string UniqueIndexName => "IX_HISTORY_VERSION";
}

internal static class MigrationExtensions
{
    public static IMigrationRunnerBuilder WithVersionTable<T>(
        this IMigrationRunnerBuilder builder) 
        where T: class, IVersionTableMetaDataAccessor
    {
        builder.Services.AddScoped<IVersionTableMetaDataAccessor, T>();
        return builder;
    }
}

and

services.AddFluentMigratorCore()
        .ConfigureRunner(o => o.AddSqlServer()
                               .WithGlobalConnectionString("...")
                               .WithVersionTable<VersionTableAccessor>()
                               .ScanIn(typeof(Startup).Assembly).For.Migrations());

What problem are you getting?

Was this page helpful?
0 / 5 - 0 ratings