Base solution for your next web application
Open Closed

EF Core Database.Migrate #4585


User avatar
0
devkev2403 created

Hi,

I am trying to trigger EF migrations using Dtabase.Migrate.

I have used this code as a reference: <a class="postlink" href="https://stackoverflow.com/questions/45123604/ef-core-migration-with-aspnetboilerplate-where-to-trigger-context-database-migra">https://stackoverflow.com/questions/451 ... base-migra</a>

But I think migration should take place before seeding - seeding may need new schema!

Currently I do this:

public class MyEntityFrameworkCoreModule : AbpModule
{
        /* Used it tests to skip dbcontext registration, in order to use in-memory database of EF Core */
        public bool SkipDbContextRegistration { get; set; }

        public bool SkipDbSeed { get; set; }

        public override void PreInitialize()
        {
            if (!SkipDbContextRegistration)
            {
                Configuration.Modules.AbpEfCore().AddDbContext<MyDbContext>(options =>
                {
                    if (options.ExistingConnection != null)
                    {
                        MyDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
                    }
                    else
                    {
                        MyDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
                    }
                });
            }
        }

        public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(typeof(MyEntityFrameworkCoreModule).GetAssembly());
        }

        public override void PostInitialize()
        {
          // THIS IS WHERE I DO MIGRATIONS
            EnsureMigrated();

            if (!SkipDbSeed)
            {
                SeedHelper.SeedHostDb(IocManager);
            }

            
        }

        private void EnsureMigrated()
        {
           // THIS IS HOW SeedHelper DOES IT
            using (var uowManager = IocManager.ResolveAsDisposable<IUnitOfWorkManager>())
            {
                using (var uow = uowManager.Object.Begin(TransactionScopeOption.Suppress))
                {
                    var context = uowManager.Object.Current.GetDbContext<MyDbContext>(MultiTenancySides.Host);

                    context.Database.Migrate();

                    uow.Complete();
                }
            }
        }

However, this code throws an exception: connection is already in a transaction and cannot participate in another transaction

Any help appreciated.


4 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    @devkev2403 can you try it with MultiTenantMigrateExecuter which is included in Migrator project. <a class="postlink" href="https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Migrator/MultiTenantMigrateExecuter.cs">https://github.com/aspnetzero/aspnet-ze ... xecuter.cs</a>

  • User Avatar
    0
    devkev2403 created

    Hi, thanks for the reply.

    The migrator tool works and that is what we currently use. However, we want the migrations to be automatically applied on our staging system so we can have Continuous Delivery. We explicitly create the migrations but want them automatically applied.

    We will be using the migrator tool on our live environment BTW! :)

  • User Avatar
    0
    devkev2403 created

    Actually, this worked! Thanks

    public class MultiTenantMigrateExecuter : ITransientDependency
        {
            public ILogger Log { get; }
    
            private readonly AbpZeroDbMigrator _migrator;
            private readonly IRepository<Tenant> _tenantRepository;
            private readonly IDbPerTenantConnectionStringResolver _connectionStringResolver;
    
            public MultiTenantMigrateExecuter(
                AbpZeroDbMigrator migrator,
                IRepository<Tenant> tenantRepository,
                ILogger log,
                IDbPerTenantConnectionStringResolver connectionStringResolver)
            {
                Log = log;
                _migrator = migrator;
                _tenantRepository = tenantRepository;
                _connectionStringResolver = connectionStringResolver;
            }
    
            public void Run()
            {
                var hostConnStr = _connectionStringResolver.GetNameOrConnectionString(new ConnectionStringResolveArgs(MultiTenancySides.Host));
                if (hostConnStr.IsNullOrWhiteSpace())
                {
                    Log.Error("Configuration file should contain a connection string named 'Default'");
                    return;
                }
                Log.Info("Host database: " + ConnectionStringHelper.GetConnectionString(hostConnStr));
    
                Log.Info("HOST database migration started...");
                try
                {
                    _migrator.CreateOrMigrateForHost(SeedHelper.SeedHostDb);
                }
                catch (Exception ex)
                {
                    Log.Error("Canceled migrations - An error occured during migration of host database.", ex);
                    return;
                }
                Log.Info("HOST database migration completed.");
    
                var migratedDatabases = new HashSet<string>();
                var tenants = _tenantRepository.GetAllList(t => t.ConnectionString != null && t.ConnectionString != "");
                for (var i = 0; i < tenants.Count; i++)
                {
                    var tenant = tenants[i];
                    Log.Info($"Tenant database migration started... ({(i + 1)} / {tenants.Count})");
                    Log.Info("Name              : " + tenant.Name);
                    Log.Info("TenancyName       : " + tenant.TenancyName);
                    Log.Info("Tenant Id         : " + tenant.Id);
                    Log.Info("Connection string : " + SimpleStringCipher.Instance.Decrypt(tenant.ConnectionString));
    
                    if (!migratedDatabases.Contains(tenant.ConnectionString))
                    {
                        try
                        {
                            _migrator.CreateOrMigrateForTenant(tenant);
                        }
                        catch (Exception ex)
                        {
                            Log.Error("An error occured during migration of tenant database. Skipped this tenant and will continue for others...", ex);
                        }
                        migratedDatabases.Add(tenant.ConnectionString);
                    }
                    else
                    {
                        Log.Info("This database has already migrated before (you have more than one tenant in same database). Skipping it....");
                    }
    
                    Log.Info($"Tenant database migration completed. ({i + 1} / {tenants.Count})");
                }
    
                Log.Info("All databases have been migrated.");
            }
        }
    
    public class MyEntityFrameworkCoreModule : AbpModule
        {
    ...
    ...
    ...
            public override void PostInitialize()
            {
                EnsureMigrated();
                if (!SkipDbSeed)
                {
                    SeedHelper.SeedHostDb(IocManager);
                }
            }
    
            private void EnsureMigrated()
            {
                using (var migrateExecuter = IocManager.ResolveAsDisposable<MultiTenantMigrateExecuter>())
                {
                    migrateExecuter.Object.Run();
                }
            }
        }
    
  • User Avatar
    0
    ismcagdas created
    Support Team

    @devkev2403 great :)