entity-framework-5dbcontextentity-framework-migrations

Custom DbContextFactory with DbMigrationsConfiguration


You'll find my custom IDbContextFactory implementation below. In my repositories I create an instance of this factory and because I'm doing a multi-tenant app (each tenant has own db) I also need to set the ConnectionString and Tenant property so that my DbContext can be created.

This all works great for if the database does not exist, but when I need to perform a migration the DbMigrationsConfiguration tries to use this factory to create an instance of EFDbContext and of course it fails because the ConnectionString and Tenant are not set.

Does anyone have an alternate strategy for working around this issue?

public class DbContextFactory : IDbContextFactory<EFDbContext>
{
    public string ConnectionString { get; set; }
    public string Tenant { get; set; }

    public EFDbContext Create()
    {
        var connectionInfo = new DbConnectionInfo(ConnectionString, "System.Data.SqlClient");

        if (EFContextHelper.Contexts.Contains(Tenant))
        {
            return new EFDbContext(ConnectionString, Tenant);
        }
        else
        {
            EFDbContext context = new EFDbContext(ConnectionString, Tenant);

            if (!context.Database.Exists())
            {
                Database.SetInitializer<EFDbContext>(new EFDbContextInitializer());
                context.Database.Initialize(false);
            }
            else
            {
                bool doMigration = false;
                try
                {
                    doMigration = !context.Database.CompatibleWithModel(true);
                }
                catch (NotSupportedException)
                {
                    // no metadata for migration
                    doMigration = true;
                }

                // not working cause migrator is trying to instantiate this factory without setting connection string etc
                if (doMigration)
                {
                    var migrationConfig = new DbMigrationsConfiguration<EFDbContext>(); 
                    migrationConfig.AutomaticMigrationDataLossAllowed = false;
                    migrationConfig.AutomaticMigrationsEnabled = true;
                    migrationConfig.TargetDatabase = connectionInfo;
                    var migrator = new DbMigrator(migrationConfig);
                    migrator.Update();
                }
            }

            EFContextHelper.Contexts.Add(Tenant);

            return context as EFDbContext;
        }
    }

Thanks, Gary


Solution

  • I managed to sort this problem out myself.

    Basically I just adjusted my DbContext class instantiation to the following:

    public EFDbContext(string connectionString, string tenant) : base(connectionString ?? "EFDBContext")
    {
         Tenant = tenant;
    }
    

    So in this case if the passed connection string is empty it just defaults to the config file connection string name. The migrator appears to keep the connection assigned in:

    migrationConfig.TargetDatabase = connectionInfo;