.net-corefluent-migrator

FluentMigrator: How to set default schema for MigrationRunner


I have a multi-tenanted database. The tenant-specific data is stored inside it's own (PostgreSQL) schema.

I'd like to be able to use FluentMigrator to deploy new tenants as required. I've set up an example on GitHub where a HTTP post to an endpoint will deploy a schema. However deploys it to the public schema by default. I'd like to be able to specify the schema to deploy to.

I.e.

public class TenantService: ITenantService {
  private readonly IServiceProvider _provider;

  public TenantService(IServiceProvider provider) {
    _provider = provider;
  }

  public void Create(string tenantName) {
    using(var scope = _provider.CreateScope()) {
      var migrationRunner = scope.ServiceProvider.GetService<IMigrationRunner>();
      // TODO:  Set the default schema = tenantName
      migrationRunner.MigrateUp();
    }
  }
}

How do I set the default schema for the MigrationRunner?

Edit: I've updated the GitHub repo to reflect the accepted answer.


Solution

  • I was struggling with this exact same issue and also the same enviroments. .Net-core, FluentMigrator and Postgre with different schema's. I have been using the Obsolete functions and allready noticed that I had to create my own: ConventionSet. I was able to fix this in the following, where I first must say: Thank you for your git, this helped me to also figure out my problem, here goes:

    First thing I did was manually create a database and a schema with a different name then: public. Then I adjusted the startup.cs

            // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IConventionSet>(new DefaultConventionSet("tenanta", null));
            services.AddFluentMigratorCore();
            services.ConfigureRunner(rb => rb
                    .AddPostgres()                    
                    .WithGlobalConnectionString("Server=localhost;Port=5432;User Id=postgres;Password=12345678;CommandTimeout=20;database=nameofdatabase")
                    .ScanIn(typeof(AddSecuritySchema).Assembly).For.Migrations());
            services.AddSingleton<ITenantService, TenantService>();
            services.AddControllers();
        }
    

    With the above change, where we add a: new DefaultConventionSet with the schemaname I could finally run migrations on a different schema then public. But I also want this to be on-the-fly. So in order to achieve that (not sure if correct) I did the following:

    public class TenantService: ITenantService {
        private readonly IServiceProvider _provider;
        public TenantService(IServiceProvider provider) {
            _provider = provider;
        }
    
        public void Create(string tenantName) {
    
            var serviceProvider = new ServiceCollection()
                            .AddSingleton<IConventionSet>(new DefaultConventionSet(tenantName, null))
                            .AddFluentMigratorCore()
                            .ConfigureRunner(r => r.AddPostgres()
                                                    .WithGlobalConnectionString("Server=localhost;Port=5432;User Id=postgres;Password=12345678;CommandTimeout=20;database=databasename")
                                                    .WithRunnerConventions(new MigrationRunnerConventions()
                                                    {
    
                                                    })
                                                    .ScanIn(typeof(AddSecuritySchema).Assembly).For.Migrations()
                            )
                            .Configure<RunnerOptions>(opt =>
                            {
                                opt.TransactionPerSession = true;
                            })
                            .BuildServiceProvider(false);
    
            using (var scope = serviceProvider.CreateScope())
            {
                var migrationRunner = scope.ServiceProvider.GetService<IMigrationRunner>();
                migrationRunner.MigrateUp();
            }
        }
    }
    

    Offcourse it's important to first register the ConventionSet ;)