mirror of https://github.com/abpframework/abp.git
committed by
GitHub
8 changed files with 146 additions and 62 deletions
@ -1,48 +0,0 @@ |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace MyCompanyName.MyProjectName.Data; |
|||
|
|||
public class DataSeederMiddleware : IMiddleware, ISingletonDependency |
|||
{ |
|||
private bool _hostSeeded; |
|||
|
|||
private readonly ILogger<DataSeederMiddleware> _logger; |
|||
private readonly IDataSeeder _dataSeeder; |
|||
|
|||
public DataSeederMiddleware( |
|||
ILogger<DataSeederMiddleware> logger, |
|||
IDataSeeder dataSeeder) |
|||
{ |
|||
_logger = logger; |
|||
_dataSeeder = dataSeeder; |
|||
} |
|||
|
|||
public async Task InvokeAsync(HttpContext context, RequestDelegate next) |
|||
{ |
|||
/* This logic is not safe if you are running multiple instances of your |
|||
* application in parallel. In that case, a distributed lock usage is suggested, |
|||
* or you can create another application for database migration/seed. |
|||
*/ |
|||
if (!_hostSeeded) |
|||
{ |
|||
await SeedHostDataAsync(); |
|||
_hostSeeded = true; |
|||
} |
|||
|
|||
await next(context); |
|||
} |
|||
|
|||
private Task SeedHostDataAsync() |
|||
{ |
|||
_logger.LogInformation($"Executing database seed..."); |
|||
|
|||
return _dataSeeder.SeedAsync(new DataSeedContext() |
|||
.WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, |
|||
IdentityDataSeedContributor.AdminEmailDefaultValue) |
|||
.WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, |
|||
IdentityDataSeedContributor.AdminPasswordDefaultValue) |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.TenantManagement; |
|||
|
|||
namespace MyCompanyName.MyProjectName.Data; |
|||
|
|||
public class MyProjectNameDbMigrationService : ITransientDependency |
|||
{ |
|||
public ILogger<MyProjectNameDbMigrationService> Logger { get; set; } |
|||
|
|||
private readonly IDataSeeder _dataSeeder; |
|||
private readonly MyProjectNameEFCoreDbSchemaMigrator _dbSchemaMigrator; |
|||
private readonly ITenantRepository _tenantRepository; |
|||
private readonly ICurrentTenant _currentTenant; |
|||
|
|||
public MyProjectNameDbMigrationService( |
|||
IDataSeeder dataSeeder, |
|||
MyProjectNameEFCoreDbSchemaMigrator dbSchemaMigrator, |
|||
ITenantRepository tenantRepository, |
|||
ICurrentTenant currentTenant) |
|||
{ |
|||
_dataSeeder = dataSeeder; |
|||
_dbSchemaMigrator = dbSchemaMigrator; |
|||
_tenantRepository = tenantRepository; |
|||
_currentTenant = currentTenant; |
|||
|
|||
Logger = NullLogger<MyProjectNameDbMigrationService>.Instance; |
|||
} |
|||
|
|||
public async Task MigrateAsync() |
|||
{ |
|||
Logger.LogInformation("Started database migrations..."); |
|||
|
|||
await MigrateDatabaseSchemaAsync(); |
|||
await SeedDataAsync(); |
|||
|
|||
Logger.LogInformation($"Successfully completed host database migrations."); |
|||
|
|||
var tenants = await _tenantRepository.GetListAsync(includeDetails: true); |
|||
|
|||
var migratedDatabaseSchemas = new HashSet<string>(); |
|||
foreach (var tenant in tenants) |
|||
{ |
|||
using (_currentTenant.Change(tenant.Id)) |
|||
{ |
|||
if (tenant.ConnectionStrings.Any()) |
|||
{ |
|||
var tenantConnectionStrings = tenant.ConnectionStrings |
|||
.Select(x => x.Value) |
|||
.ToList(); |
|||
|
|||
if (!migratedDatabaseSchemas.IsSupersetOf(tenantConnectionStrings)) |
|||
{ |
|||
await MigrateDatabaseSchemaAsync(tenant); |
|||
|
|||
migratedDatabaseSchemas.AddIfNotContains(tenantConnectionStrings); |
|||
} |
|||
} |
|||
|
|||
await SeedDataAsync(tenant); |
|||
} |
|||
|
|||
Logger.LogInformation($"Successfully completed {tenant.Name} tenant database migrations."); |
|||
} |
|||
|
|||
Logger.LogInformation("Successfully completed all database migrations."); |
|||
Logger.LogInformation("You can safely end this process..."); |
|||
} |
|||
|
|||
private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null) |
|||
{ |
|||
Logger.LogInformation($"Migrating schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database..."); |
|||
await _dbSchemaMigrator.MigrateAsync(); |
|||
} |
|||
|
|||
private async Task SeedDataAsync(Tenant tenant = null) |
|||
{ |
|||
Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); |
|||
|
|||
await _dataSeeder.SeedAsync(new DataSeedContext(tenant?.Id) |
|||
.WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, IdentityDataSeedContributor.AdminEmailDefaultValue) |
|||
.WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, IdentityDataSeedContributor.AdminPasswordDefaultValue) |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace MyCompanyName.MyProjectName.Data; |
|||
|
|||
public class MyProjectNameEFCoreDbSchemaMigrator : ITransientDependency |
|||
{ |
|||
private readonly IServiceProvider _serviceProvider; |
|||
|
|||
public MyProjectNameEFCoreDbSchemaMigrator( |
|||
IServiceProvider serviceProvider) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
} |
|||
|
|||
public async Task MigrateAsync() |
|||
{ |
|||
/* We intentionally resolving the MyProjectNameDbContext |
|||
* from IServiceProvider (instead of directly injecting it) |
|||
* to properly get the connection string of the current tenant in the |
|||
* current scope. |
|||
*/ |
|||
|
|||
await _serviceProvider |
|||
.GetRequiredService<MyProjectNameDbContext>() |
|||
.Database |
|||
.MigrateAsync(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue