Open Closed

Apply Migrations for new Tenant and migrate multiple databases #2936


User avatar
0
Yaduraj created

We have solution that contains multiple microservices. One of them contains host entities and data others are tenant specific entity and services. We haave DbMigrator console app in our host project only. Now we are able to run DbMigrator as console app in development and migrate host and tenant database that is:

  • Creates Host database
  • Creates Tenant database with same Db Schema as Host with default Admin user
  • For other services, We create database using Script Migration

How to run this DbMigrator in a Production server. Can we control DbMigrator to migrate all the databases we need to migrate? We see there is option to "Apply Database Migrations" in Saas module but it does not seem to work. What sort of implementation it requires?

  • ABP Framework version: v4.3.1
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): yes
  • Exception message and stack trace:
  • Steps to reproduce the issue:"

3 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi

    How to run this DbMigrator in a Production server.

    DbMigrator project is a console application, you can directly use dotnet command to run it.

    Can we control DbMigrator to migrate all the databases we need to migrate?

    Of course, in fact, the DbMigrator will call MigrateAsync method of I<Project>DbSchemaMigrator. you can create ....DbSchemaMigrator Implementation class for each service.

  • User Avatar
    0
    Yaduraj created

    Thanks a lot.

    And also can you give some light on:

    We see there is option to "Apply Database Migrations" in Saas module but it does not seem to work? How to enable or implement this feature?

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    We see there is option to "Apply Database Migrations" in Saas module but it does not seem to work?

    Apply Database Migrations will publish an event with ApplyDatabaseMigrationsEto.

    You can create an event handler to create the database. for example:

    public class MyProjectNameTenantDatabaseMigrationHandler :
        IDistributedEventHandler<ApplyDatabaseMigrationsEto>,
        ITransientDependency
    {
        private readonly IEnumerable<IMyProjectNameDbSchemaMigrator> _dbSchemaMigrators;
        private readonly ICurrentTenant _currentTenant;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IDataSeeder _dataSeeder;
        private readonly ITenantStore _tenantStore;
        private readonly ILogger<MyProjectNameTenantDatabaseMigrationHandler> _logger;
    
        public MyProjectNameTenantDatabaseMigrationHandler(
            IEnumerable<IMyProjectNameDbSchemaMigrator> dbSchemaMigrators,
            ICurrentTenant currentTenant,
            IUnitOfWorkManager unitOfWorkManager,
            IDataSeeder dataSeeder,
            ITenantStore tenantStore,
            ILogger<MyProjectNameTenantDatabaseMigrationHandler> logger)
        {
            _dbSchemaMigrators = dbSchemaMigrators;
            _currentTenant = currentTenant;
            _unitOfWorkManager = unitOfWorkManager;
            _dataSeeder = dataSeeder;
            _tenantStore = tenantStore;
            _logger = logger;
        }
    
        public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
        {
            if (eventData.TenantId == null)
            {
                return;
            }
    
            await MigrateAndSeedForTenantAsync(
                eventData.TenantId.Value,
                MyProjectNameConsts.AdminEmailDefaultValue,
                MyProjectNameConsts.AdminPasswordDefaultValue
            );
        }
    
        private async Task MigrateAndSeedForTenantAsync(
            Guid tenantId,
            string adminEmail,
            string adminPassword)
        {
            try
            {
                using (_currentTenant.Change(tenantId))
                {
                    // Create database tables if needed
                    using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: false))
                    {
                        var tenantConfiguration = await _tenantStore.FindAsync(tenantId);
                        if (tenantConfiguration?.ConnectionStrings != null &&
                            !tenantConfiguration.ConnectionStrings.Default.IsNullOrWhiteSpace())
                        {
                            foreach (var migrator in _dbSchemaMigrators)
                            {
                                await migrator.MigrateAsync();
                            }
                        }
    
                        await uow.CompleteAsync();
                    }
    
                    // Seed data
                    using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
                    {
                        await _dataSeeder.SeedAsync(
                            new DataSeedContext(tenantId)
                                .WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, adminEmail)
                                .WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, adminPassword)
                        );
    
                        await uow.CompleteAsync();
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogException(ex);
            }
        }
    }
    
Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11