diff --git a/microservices/KonSoft.Admin.HttpApi.Host/DbMigrations/AdminDataSeeder.cs b/microservices/KonSoft.Admin.HttpApi.Host/DbMigrations/AdminDataSeeder.cs new file mode 100644 index 0000000..5b125bf --- /dev/null +++ b/microservices/KonSoft.Admin.HttpApi.Host/DbMigrations/AdminDataSeeder.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; + +namespace KonSoft.Admin.DbMigrations +{ + public class AdminDataSeeder : IDataSeedContributor, ITransientDependency + { + public Task SeedAsync(DataSeedContext context) + { + return Task.CompletedTask; + } + } +} diff --git a/microservices/KonSoft.Admin.HttpApi.Host/DbMigrations/AdminPendingEfCoreMigrationsChecker.cs b/microservices/KonSoft.Admin.HttpApi.Host/DbMigrations/AdminPendingEfCoreMigrationsChecker.cs new file mode 100644 index 0000000..508e8be --- /dev/null +++ b/microservices/KonSoft.Admin.HttpApi.Host/DbMigrations/AdminPendingEfCoreMigrationsChecker.cs @@ -0,0 +1,26 @@ +using System; +using KonSoft.Admin.EntityFrameworkCore; +using KonSoft.Shared.Hosting.Microservices.DbMigrations.EfCore; +using Volo.Abp.DistributedLocking; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; + +namespace KonSoft.Admin.DbMigrations +{ + public class AdminPendingEfCoreMigrationsChecker : PendingEfCoreMigrationsChecker + { + public AdminPendingEfCoreMigrationsChecker(IUnitOfWorkManager unitOfWorkManager, + IServiceProvider serviceProvider, + ICurrentTenant currentTenant, + IDistributedEventBus distributedEventBus, + IAbpDistributedLock abpDistributedLock) : base(unitOfWorkManager, + serviceProvider, + currentTenant, + distributedEventBus, + abpDistributedLock, + "Clean") + { + } + } +} diff --git a/microservices/KonSoft.Admin.HttpApi.Host/KonSoft.Admin.HttpApi.Host.csproj b/microservices/KonSoft.Admin.HttpApi.Host/KonSoft.Admin.HttpApi.Host.csproj index 9022022..5debc82 100644 --- a/microservices/KonSoft.Admin.HttpApi.Host/KonSoft.Admin.HttpApi.Host.csproj +++ b/microservices/KonSoft.Admin.HttpApi.Host/KonSoft.Admin.HttpApi.Host.csproj @@ -1,4 +1,4 @@ - + net8.0 diff --git a/shared/KonSoft.Shared.Hosting.Microservices/DbMigrations/EfCore/PendingEfCoreMigrationsChecker.cs b/shared/KonSoft.Shared.Hosting.Microservices/DbMigrations/EfCore/PendingEfCoreMigrationsChecker.cs new file mode 100644 index 0000000..2579c4c --- /dev/null +++ b/shared/KonSoft.Shared.Hosting.Microservices/DbMigrations/EfCore/PendingEfCoreMigrationsChecker.cs @@ -0,0 +1,109 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Serilog; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DistributedLocking; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Uow; + +namespace KonSoft.Shared.Hosting.Microservices.DbMigrations.EfCore +{ + public abstract class PendingEfCoreMigrationsChecker : ITransientDependency + where TDbContext : DbContext + { + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected IServiceProvider ServiceProvider { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IDistributedEventBus DistributedEventBus { get; } + protected IAbpDistributedLock DistributedLockProvider { get; } + protected string DatabaseName { get; } + + protected PendingEfCoreMigrationsChecker( + IUnitOfWorkManager unitOfWorkManager, + IServiceProvider serviceProvider, + ICurrentTenant currentTenant, + IDistributedEventBus distributedEventBus, + IAbpDistributedLock abpDistributedLock, + string databaseName) + { + UnitOfWorkManager = unitOfWorkManager; + ServiceProvider = serviceProvider; + CurrentTenant = currentTenant; + DistributedEventBus = distributedEventBus; + DistributedLockProvider = abpDistributedLock; + DatabaseName = databaseName; + } + + public virtual async Task CheckAndApplyDatabaseMigrationsAsync() + { + await TryAsync(LockAndApplyDatabaseMigrationsAsync); + } + + protected async Task TryAsync(Func task, int retryCount = 3) + { + try + { + await task(); + } + catch (Exception ex) + { + retryCount--; + + if (retryCount <= 0) + { + throw; + } + + Log.Warning($"{ex.GetType().Name} has been thrown. The operation will be tried {retryCount} times more. Exception:\n{ex.Message}"); + + await Task.Delay(RandomHelper.GetRandom(5000, 15000)); + + await TryAsync(task, retryCount); + } + } + + protected virtual async Task LockAndApplyDatabaseMigrationsAsync() + { + await using (var handle = await DistributedLockProvider.TryAcquireAsync("Migration_" + DatabaseName)) + { + Log.Information($"Lock is acquired for db migration and seeding on database named: {DatabaseName}..."); + + if (handle is null) + { + Log.Information($"Handle is null because of the locking for : {DatabaseName}"); + return; + } + + using (CurrentTenant.Change(null)) + { + // Create database tables if needed + using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false)) + { + var dbContext = ServiceProvider.GetRequiredService(); + + var pendingMigrations = await dbContext + .Database + .GetPendingMigrationsAsync(); + + if (pendingMigrations.Any()) + { + await dbContext.Database.MigrateAsync(); + } + + await uow.CompleteAsync(); + } + + await ServiceProvider.GetRequiredService() + .SeedAsync(); + } + Log.Information($"Lock is released for db migration and seeding on database named: {DatabaseName}..."); + } + } + } +}