Merge pull request #11386 from abpframework/MigrateDatabase

Improve data seeding for the single-layer startup template.
pull/11405/head
Halil İbrahim Kalkan 3 years ago committed by GitHub
commit dbb014b509
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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();
}
}

@ -103,8 +103,4 @@
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Entities" />
<Folder Include="Services\Dtos" />
</ItemGroup>
</Project>

@ -288,7 +288,6 @@ public class MyProjectNameModule : AbpModule
app.UseErrorPage();
}
app.UseMiddleware<DataSeederMiddleware>();
app.UseCorrelationId();
app.UseStaticFiles();
app.UseRouting();

@ -1,26 +1,35 @@
using MyCompanyName.MyProjectName.Data;
using Serilog;
using Serilog.Events;
namespace MyCompanyName.MyProjectName;
public class Program
{
public async static Task<int> Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
var loggerConfiguration = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
#if DEBUG
.WriteTo.Async(c => c.Console())
.WriteTo.Async(c => c.Console());
#endif
.CreateLogger();
if (IsMigrateDatabase(args))
{
loggerConfiguration.MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning);
loggerConfiguration.MinimumLevel.Override("Microsoft", LogEventLevel.Warning);
loggerConfiguration.MinimumLevel.Override("IdentityServer4.Startup", LogEventLevel.Warning);
}
Log.Logger = loggerConfiguration.CreateLogger();
try
{
@ -32,6 +41,12 @@ public class Program
var app = builder.Build();
await app.InitializeApplicationAsync();
if (IsMigrateDatabase(args))
{
await app.Services.GetRequiredService<MyProjectNameDbMigrationService>().MigrateAsync();
return 0;
}
Log.Information("Starting MyCompanyName.MyProjectName.");
await app.RunAsync();
return 0;
@ -51,4 +66,9 @@ public class Program
Log.CloseAndFlush();
}
}
private static bool IsMigrateDatabase(string[] args)
{
return args.Any(x => x.Contains("--migrate-database", StringComparison.OrdinalIgnoreCase));
}
}

Loading…
Cancel
Save