using System; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Serilog; 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 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; } protected IUnitOfWorkManager UnitOfWorkManager { get; } protected IServiceProvider ServiceProvider { get; } protected ICurrentTenant CurrentTenant { get; } protected IDistributedEventBus DistributedEventBus { get; } protected IAbpDistributedLock DistributedLockProvider { get; } protected string DatabaseName { get; } 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(true, 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}..."); } }