31 changed files with 6286 additions and 435 deletions
@ -1,209 +0,0 @@
|
||||
using Microsoft.Extensions.Logging; |
||||
using Microsoft.Extensions.Logging.Abstractions; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Runtime.InteropServices; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Data; |
||||
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.Identity; |
||||
using Volo.Abp.MultiTenancy; |
||||
using Volo.Abp.TenantManagement; |
||||
|
||||
namespace BookStore.Data; |
||||
|
||||
public class BookStoreDbMigrationService : ITransientDependency |
||||
{ |
||||
public ILogger<BookStoreDbMigrationService> Logger { get; set; } |
||||
|
||||
private readonly IDataSeeder _dataSeeder; |
||||
private readonly BookStoreEFCoreDbSchemaMigrator _dbSchemaMigrator; |
||||
private readonly ITenantRepository _tenantRepository; |
||||
private readonly ICurrentTenant _currentTenant; |
||||
|
||||
public BookStoreDbMigrationService( |
||||
IDataSeeder dataSeeder, |
||||
BookStoreEFCoreDbSchemaMigrator dbSchemaMigrator, |
||||
ITenantRepository tenantRepository, |
||||
ICurrentTenant currentTenant) |
||||
{ |
||||
_dataSeeder = dataSeeder; |
||||
_dbSchemaMigrator = dbSchemaMigrator; |
||||
_tenantRepository = tenantRepository; |
||||
_currentTenant = currentTenant; |
||||
|
||||
Logger = NullLogger<BookStoreDbMigrationService>.Instance; |
||||
} |
||||
|
||||
public async Task MigrateAsync() |
||||
{ |
||||
var initialMigrationAdded = AddInitialMigrationIfNotExist(); |
||||
|
||||
if (initialMigrationAdded) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
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) |
||||
); |
||||
} |
||||
|
||||
private bool AddInitialMigrationIfNotExist() |
||||
{ |
||||
try |
||||
{ |
||||
if (!DbMigrationsProjectExists()) |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
catch (Exception) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
try |
||||
{ |
||||
if (!MigrationsFolderExists()) |
||||
{ |
||||
AddInitialMigration(); |
||||
return true; |
||||
} |
||||
else |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
Logger.LogWarning("Couldn't determinate if any migrations exist : " + e.Message); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
private bool DbMigrationsProjectExists() |
||||
{ |
||||
return Directory.Exists(GetEntityFrameworkCoreProjectFolderPath()); |
||||
} |
||||
|
||||
private bool MigrationsFolderExists() |
||||
{ |
||||
var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath(); |
||||
|
||||
return Directory.Exists(Path.Combine(dbMigrationsProjectFolder, "Migrations")); |
||||
} |
||||
|
||||
private void AddInitialMigration() |
||||
{ |
||||
Logger.LogInformation("Creating initial migration..."); |
||||
|
||||
string argumentPrefix; |
||||
string fileName; |
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
||||
{ |
||||
argumentPrefix = "-c"; |
||||
fileName = "/bin/bash"; |
||||
} |
||||
else |
||||
{ |
||||
argumentPrefix = "/C"; |
||||
fileName = "cmd.exe"; |
||||
} |
||||
|
||||
var procStartInfo = new ProcessStartInfo(fileName, |
||||
$"{argumentPrefix} \"abp create-migration-and-run-migrator \"{GetEntityFrameworkCoreProjectFolderPath()}\" --nolayers\"" |
||||
); |
||||
|
||||
try |
||||
{ |
||||
Process.Start(procStartInfo); |
||||
} |
||||
catch (Exception) |
||||
{ |
||||
throw new Exception("Couldn't run ABP CLI..."); |
||||
} |
||||
} |
||||
|
||||
private string GetEntityFrameworkCoreProjectFolderPath() |
||||
{ |
||||
var slnDirectoryPath = GetSolutionDirectoryPath(); |
||||
|
||||
if (slnDirectoryPath == null) |
||||
{ |
||||
throw new Exception("Solution folder not found!"); |
||||
} |
||||
|
||||
return Path.Combine(slnDirectoryPath, "BookStore"); |
||||
} |
||||
|
||||
private string GetSolutionDirectoryPath() |
||||
{ |
||||
var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory()); |
||||
|
||||
while (Directory.GetParent(currentDirectory.FullName) != null) |
||||
{ |
||||
currentDirectory = Directory.GetParent(currentDirectory.FullName); |
||||
|
||||
if (Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) |
||||
{ |
||||
return currentDirectory.FullName; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
@ -1,32 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore; |
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using System; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.DependencyInjection; |
||||
|
||||
namespace BookStore.Data; |
||||
|
||||
public class BookStoreEFCoreDbSchemaMigrator : ITransientDependency |
||||
{ |
||||
private readonly IServiceProvider _serviceProvider; |
||||
|
||||
public BookStoreEFCoreDbSchemaMigrator( |
||||
IServiceProvider serviceProvider) |
||||
{ |
||||
_serviceProvider = serviceProvider; |
||||
} |
||||
|
||||
public async Task MigrateAsync() |
||||
{ |
||||
/* We intentionally resolving the BookStoreDbContext |
||||
* from IServiceProvider (instead of directly injecting it) |
||||
* to properly get the connection string of the current tenant in the |
||||
* current scope. |
||||
*/ |
||||
|
||||
await _serviceProvider |
||||
.GetRequiredService<BookStoreDbContext>() |
||||
.Database |
||||
.MigrateAsync(); |
||||
} |
||||
} |
@ -0,0 +1,21 @@
|
||||
using Microsoft.Extensions.Hosting; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using Volo.Abp.Data; |
||||
|
||||
namespace BookStore.DataSeeder; |
||||
|
||||
public class BookStoreDataSeederWorker : BackgroundService |
||||
{ |
||||
protected IDataSeeder DataSeeder { get; } |
||||
|
||||
public BookStoreDataSeederWorker(IDataSeeder dataSeeder) |
||||
{ |
||||
DataSeeder = dataSeeder; |
||||
} |
||||
|
||||
protected async override Task ExecuteAsync(CancellationToken stoppingToken) |
||||
{ |
||||
await DataSeeder.SeedAsync(); |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "ar", |
||||
"texts": { |
||||
"Welcome_Title": "مرحبا", |
||||
"Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", |
||||
"Menu:Home": "الرئيسية" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "cs", |
||||
"texts": { |
||||
"Welcome_Title": "Vítejte", |
||||
"Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", |
||||
"Menu:Home": "Úvod" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "de-DE", |
||||
"texts": { |
||||
"Welcome_Title": "Willkommen", |
||||
"Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "en-GB", |
||||
"texts": { |
||||
"Welcome_Title": "Welcome_Title", |
||||
"Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "es", |
||||
"texts": { |
||||
"Welcome_Title": "Bienvenido", |
||||
"Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", |
||||
"Menu:Home": "Inicio" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "fi", |
||||
"texts": { |
||||
"Welcome_Title": "Tervetuloa", |
||||
"Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", |
||||
"Menu:Home": "Koti" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "fr", |
||||
"texts": { |
||||
"Welcome_Title": "Bienvenue", |
||||
"Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", |
||||
"Menu:Home": "Accueil" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "hu", |
||||
"texts": { |
||||
"Welcome_Title": "Üdvözlöm", |
||||
"Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", |
||||
"Menu:Home": "Kezdőlap" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "is", |
||||
"texts": { |
||||
"Welcome_Title": "Velkomin", |
||||
"Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", |
||||
"Menu:Home": "Heim" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "it", |
||||
"texts": { |
||||
"Welcome_Title": "Benvenuto", |
||||
"Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "nl", |
||||
"texts": { |
||||
"Welcome_Title": "Welkom", |
||||
"Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "pl-PL", |
||||
"texts": { |
||||
"Welcome_Title": "Witaj", |
||||
"Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "pt-BR", |
||||
"texts": { |
||||
"Welcome_Title": "Seja bem-vindo!", |
||||
"Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", |
||||
"Menu:Home": "Principal" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "ro-RO", |
||||
"texts": { |
||||
"Welcome_Title": "Bun venit", |
||||
"Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", |
||||
"Menu:Home": "Acasă" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "ru", |
||||
"texts": { |
||||
"Welcome_Title": "Bine ati venit", |
||||
"Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", |
||||
"Menu:Home": "Главная" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "sk", |
||||
"texts": { |
||||
"Welcome_Title": "Vitajte", |
||||
"Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", |
||||
"Menu:Home": "Domov" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "sl", |
||||
"texts": { |
||||
"Welcome_Title": "Dobrodošli", |
||||
"Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", |
||||
"Menu:Home": "Domov" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "tr", |
||||
"texts": { |
||||
"Welcome_Title": "Hoşgeldiniz", |
||||
"Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", |
||||
"Menu:Home": "Ana sayfa" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "vi", |
||||
"texts": { |
||||
"Welcome_Title": "Chào mừng bạn", |
||||
"Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", |
||||
"Menu:Home": "Trang chủ" |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
{ |
||||
"culture": "zh-Hant", |
||||
"texts": { |
||||
"Welcome_Title": "歡迎", |
||||
"Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", |
||||
"Menu:Home": "首頁" |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue