46 changed files with 745 additions and 624 deletions
@ -1,25 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace>EShopOnAbp.CatalogService</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\EShopOnAbp.CatalogService.Domain\EShopOnAbp.CatalogService.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.EntityFrameworkCore.PostgreSql" Version="5.0.0-rc.1" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> |
|||
<PrivateAssets>compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native</PrivateAssets> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -1,34 +0,0 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace EShopOnAbp.CatalogService.EntityFrameworkCore |
|||
{ |
|||
[ConnectionStringName(CatalogServiceDbProperties.ConnectionStringName)] |
|||
public class CatalogServiceDbContext : |
|||
AbpDbContext<CatalogServiceDbContext> |
|||
{ |
|||
/* Add DbSet properties for your Aggregate Roots / Entities here. */ |
|||
|
|||
public CatalogServiceDbContext(DbContextOptions<CatalogServiceDbContext> options) |
|||
: base(options) |
|||
{ |
|||
} |
|||
|
|||
protected override void OnModelCreating(ModelBuilder builder) |
|||
{ |
|||
base.OnModelCreating(builder); |
|||
|
|||
/* Include modules to your migration db context */ |
|||
|
|||
/* Configure your own tables/entities inside here */ |
|||
|
|||
//builder.Entity<YourEntity>(b =>
|
|||
//{
|
|||
// b.ToTable(CatalogServiceConsts.DbTablePrefix + "YourEntities", CatalogServiceConsts.DbSchema);
|
|||
// b.ConfigureByConvention(); //auto configure for the base class props
|
|||
// //...
|
|||
//});
|
|||
} |
|||
} |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
using System.IO; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Design; |
|||
using Microsoft.Extensions.Configuration; |
|||
|
|||
namespace EShopOnAbp.CatalogService.EntityFrameworkCore |
|||
{ |
|||
/* This class is needed for EF Core console commands |
|||
* (like Add-Migration and Update-Database commands) */ |
|||
public class CatalogServiceDbContextFactory : IDesignTimeDbContextFactory<CatalogServiceDbContext> |
|||
{ |
|||
public CatalogServiceDbContext CreateDbContext(string[] args) |
|||
{ |
|||
CatalogServiceEfCoreEntityExtensionMappings.Configure(); |
|||
|
|||
var configuration = BuildConfiguration(); |
|||
|
|||
var builder = new DbContextOptionsBuilder<CatalogServiceDbContext>() |
|||
.UseNpgsql( |
|||
configuration.GetConnectionString(CatalogServiceDbProperties.ConnectionStringName), |
|||
b => |
|||
{ |
|||
b.MigrationsHistoryTable("__CatalogService_Migrations"); |
|||
}); |
|||
|
|||
return new CatalogServiceDbContext(builder.Options); |
|||
} |
|||
|
|||
private static IConfigurationRoot BuildConfiguration() |
|||
{ |
|||
var builder = new ConfigurationBuilder() |
|||
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../EShopOnAbp.CatalogService.HttpApi.Host")) |
|||
.AddJsonFile("appsettings.json", optional: false); |
|||
|
|||
return builder.Build(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace EShopOnAbp.CatalogService.EntityFrameworkCore |
|||
{ |
|||
public static class CatalogServiceEfCoreEntityExtensionMappings |
|||
{ |
|||
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); |
|||
|
|||
public static void Configure() |
|||
{ |
|||
OneTimeRunner.Run(() => |
|||
{ |
|||
/* You can configure extra properties for the |
|||
* entities defined in the modules used by your application. |
|||
* |
|||
* This class can be used to map these extra properties to table fields in the database. |
|||
* |
|||
* USE THIS CLASS ONLY TO CONFIGURE EF CORE RELATED MAPPING. |
|||
* USE CatalogServiceModuleExtensionConfigurator CLASS (in the Domain.Shared project) |
|||
* FOR A HIGH LEVEL API TO DEFINE EXTRA PROPERTIES TO ENTITIES OF THE USED MODULES |
|||
* |
|||
* Example: Map a property to a table field: |
|||
|
|||
ObjectExtensionManager.Instance |
|||
.MapEfCoreProperty<IdentityUser, string>( |
|||
"MyProperty", |
|||
(entityBuilder, propertyBuilder) => |
|||
{ |
|||
propertyBuilder.HasMaxLength(128); |
|||
} |
|||
); |
|||
|
|||
* See the documentation for more: |
|||
* https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities
|
|||
*/ |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,37 +0,0 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore.PostgreSql; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EShopOnAbp.CatalogService.EntityFrameworkCore |
|||
{ |
|||
[DependsOn( |
|||
typeof(CatalogServiceDomainModule), |
|||
typeof(AbpEntityFrameworkCorePostgreSqlModule) |
|||
)] |
|||
public class CatalogServiceEntityFrameworkCoreModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
CatalogServiceEfCoreEntityExtensionMappings.Configure(); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAbpDbContext<CatalogServiceDbContext>(options => |
|||
{ |
|||
/* Remove "includeAllEntities: true" to create |
|||
* default repositories only for aggregate roots */ |
|||
options.AddDefaultRepositories(includeAllEntities: true); |
|||
}); |
|||
|
|||
Configure<AbpDbContextOptions>(options => |
|||
{ |
|||
options.UseNpgsql(b => |
|||
{ |
|||
b.MigrationsHistoryTable("__CatalogService_Migrations"); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
// <auto-generated />
|
|||
using EShopOnAbp.CatalogService.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace EShopOnAbp.CatalogService.Migrations |
|||
{ |
|||
[DbContext(typeof(CatalogServiceDbContext))] |
|||
[Migration("20211125124215_Initial")] |
|||
partial class Initial |
|||
{ |
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) |
|||
.HasAnnotation("ProductVersion", "6.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 63); |
|||
|
|||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace EShopOnAbp.CatalogService.Migrations |
|||
{ |
|||
public partial class Initial : Migration |
|||
{ |
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
|
|||
} |
|||
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
// <auto-generated />
|
|||
using EShopOnAbp.CatalogService.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace EShopOnAbp.CatalogService.Migrations |
|||
{ |
|||
[DbContext(typeof(CatalogServiceDbContext))] |
|||
partial class CatalogServiceDbContextModelSnapshot : ModelSnapshot |
|||
{ |
|||
protected override void BuildModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) |
|||
.HasAnnotation("ProductVersion", "6.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 63); |
|||
|
|||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
|||
@ -1,2 +0,0 @@ |
|||
using System.Runtime.CompilerServices; |
|||
[assembly:InternalsVisibleToAttribute("EShopOnAbp.CatalogService.EntityFrameworkCore.Tests")] |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace>EShopOnAbp.CatalogService</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.MongoDB" Version="5.0.0-rc.1" /> |
|||
<ProjectReference Include="..\EShopOnAbp.CatalogService.Domain\EShopOnAbp.CatalogService.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,20 @@ |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.MongoDB; |
|||
|
|||
namespace EShopOnAbp.CatalogService.MongoDB |
|||
{ |
|||
[ConnectionStringName(CatalogServiceDbProperties.ConnectionStringName)] |
|||
public class CatalogServiceMongoDbContext : AbpMongoDbContext, ICatalogServiceMongoDbContext |
|||
{ |
|||
/* Add mongo collections here. Example: |
|||
* public IMongoCollection<Question> Questions => Collection<Question>(); |
|||
*/ |
|||
|
|||
protected override void CreateModel(IMongoModelBuilder modelBuilder) |
|||
{ |
|||
base.CreateModel(modelBuilder); |
|||
|
|||
modelBuilder.ConfigureCatalogService(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using Volo.Abp; |
|||
using Volo.Abp.MongoDB; |
|||
|
|||
namespace EShopOnAbp.CatalogService.MongoDB |
|||
{ |
|||
public static class CatalogServiceMongoDbContextExtensions |
|||
{ |
|||
public static void ConfigureCatalogService( |
|||
this IMongoModelBuilder builder) |
|||
{ |
|||
Check.NotNull(builder, nameof(builder)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.MongoDB; |
|||
|
|||
namespace EShopOnAbp.CatalogService.MongoDB |
|||
{ |
|||
[DependsOn( |
|||
typeof(CatalogServiceDomainModule), |
|||
typeof(AbpMongoDbModule) |
|||
)] |
|||
public class CatalogServiceMongoDbModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddMongoDbContext<CatalogServiceMongoDbContext>(options => |
|||
{ |
|||
/* Add custom repositories here. Example: |
|||
* options.AddRepository<Question, MongoQuestionRepository>(); |
|||
*/ |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.MongoDB; |
|||
|
|||
namespace EShopOnAbp.CatalogService.MongoDB |
|||
{ |
|||
[ConnectionStringName(CatalogServiceDbProperties.ConnectionStringName)] |
|||
public interface ICatalogServiceMongoDbContext : IAbpMongoDbContext |
|||
{ |
|||
/* Define mongo collections here. Example: |
|||
* IMongoCollection<Question> Questions { get; } |
|||
*/ |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using EShopOnAbp.CatalogService.MongoDB; |
|||
using Xunit; |
|||
|
|||
namespace EShopOnAbp.CatalogService; |
|||
|
|||
[CollectionDefinition(CatalogServiceTestConsts.CollectionDefinitionName)] |
|||
public class CatalogServiceDomainCollection : CatalogServiceMongoDbCollectionFixtureBase |
|||
{ |
|||
|
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
using Volo.Abp; |
|||
|
|||
namespace EShopOnAbp.CatalogService.EntityFrameworkCore |
|||
{ |
|||
public abstract class CatalogServiceEntityFrameworkCoreTestBase : CatalogServiceTestBase<CatalogServiceEntityFrameworkCoreTestModule> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -1,62 +0,0 @@ |
|||
using Microsoft.Data.Sqlite; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Storage; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore.Sqlite; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EShopOnAbp.CatalogService.EntityFrameworkCore |
|||
{ |
|||
[DependsOn( |
|||
typeof(CatalogServiceEntityFrameworkCoreModule), |
|||
typeof(CatalogServiceTestBaseModule), |
|||
typeof(AbpEntityFrameworkCoreSqliteModule) |
|||
)] |
|||
public class CatalogServiceEntityFrameworkCoreTestModule : AbpModule |
|||
{ |
|||
private SqliteConnection _sqliteConnection; |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
ConfigureInMemorySqlite(context.Services); |
|||
} |
|||
|
|||
private void ConfigureInMemorySqlite(IServiceCollection services) |
|||
{ |
|||
_sqliteConnection = CreateDatabaseAndGetConnection(); |
|||
|
|||
services.Configure<AbpDbContextOptions>(options => |
|||
{ |
|||
options.Configure(context => |
|||
{ |
|||
context.DbContextOptions.UseSqlite(_sqliteConnection); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
public override void OnApplicationShutdown(ApplicationShutdownContext context) |
|||
{ |
|||
_sqliteConnection.Dispose(); |
|||
} |
|||
|
|||
private static SqliteConnection CreateDatabaseAndGetConnection() |
|||
{ |
|||
var connection = new SqliteConnection("Data Source=:memory:"); |
|||
connection.Open(); |
|||
|
|||
var options = new DbContextOptionsBuilder<CatalogServiceDbContext>() |
|||
.UseSqlite(connection) |
|||
.Options; |
|||
|
|||
using (var context = new CatalogServiceDbContext(options)) |
|||
{ |
|||
context.GetService<IRelationalDatabaseCreator>().CreateTables(); |
|||
} |
|||
|
|||
return connection; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Xunit; |
|||
|
|||
namespace EShopOnAbp.CatalogService.MongoDB; |
|||
|
|||
[CollectionDefinition(CatalogServiceTestConsts.CollectionDefinitionName)] |
|||
public class CatalogServiceMongoCollection : CatalogServiceMongoDbCollectionFixtureBase |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Xunit; |
|||
|
|||
namespace EShopOnAbp.CatalogService.MongoDB; |
|||
|
|||
public class CatalogServiceMongoDbCollectionFixtureBase : ICollectionFixture<CatalogServiceMongoDbFixture> |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using Mongo2Go; |
|||
|
|||
namespace EShopOnAbp.CatalogService.MongoDB |
|||
{ |
|||
public class CatalogServiceMongoDbFixture : IDisposable |
|||
{ |
|||
private static readonly MongoDbRunner MongoDbRunner; |
|||
public static readonly string ConnectionString; |
|||
|
|||
static CatalogServiceMongoDbFixture() |
|||
{ |
|||
MongoDbRunner = MongoDbRunner.Start(singleNodeReplSet: true, singleNodeReplSetWaitTimeout: 20); |
|||
ConnectionString = MongoDbRunner.ConnectionString; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
MongoDbRunner?.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
namespace EShopOnAbp.CatalogService.MongoDB |
|||
{ |
|||
/* This class can be used as a base class for MongoDB integration tests, |
|||
* while SampleRepository_Tests uses a different approach. |
|||
*/ |
|||
public abstract class CatalogServiceMongoDbTestBase : CatalogServiceTestBase<CatalogServiceMongoDbTestModule> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EShopOnAbp.CatalogService.MongoDB |
|||
{ |
|||
[DependsOn( |
|||
typeof(CatalogServiceTestBaseModule), |
|||
typeof(CatalogServiceMongoDbModule) |
|||
)] |
|||
public class CatalogServiceMongoDbTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var stringArray = CatalogServiceMongoDbFixture.ConnectionString.Split('?'); |
|||
var connectionString = stringArray[0].EnsureEndsWith('/') + |
|||
"Db_" + |
|||
Guid.NewGuid().ToString("N") + "/?" + stringArray[1]; |
|||
|
|||
Configure<AbpDbConnectionOptions>(options => |
|||
{ |
|||
options.ConnectionStrings.Default = connectionString; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace EShopOnAbp.CatalogService; |
|||
|
|||
public static class CatalogServiceTestConsts |
|||
{ |
|||
public const string CollectionDefinitionName = "CatalogService collection"; |
|||
} |
|||
@ -1,185 +1,7 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Serilog; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations |
|||
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations; |
|||
|
|||
public abstract class DatabaseMigrationEventHandlerBase : ITransientDependency |
|||
{ |
|||
public abstract class DatabaseMigrationEventHandlerBase<TDbContext> : |
|||
ITransientDependency |
|||
where TDbContext : DbContext, IEfCoreDbContext |
|||
{ |
|||
protected const string TryCountPropertyName = "TryCount"; |
|||
protected const int MaxEventTryCount = 3; |
|||
|
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected ITenantStore TenantStore { get; } |
|||
protected IDistributedEventBus DistributedEventBus { get; } |
|||
protected ILogger<DatabaseMigrationEventHandlerBase<TDbContext>> Logger { get; set; } |
|||
protected string DatabaseName { get; } |
|||
|
|||
protected DatabaseMigrationEventHandlerBase( |
|||
ICurrentTenant currentTenant, |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
ITenantStore tenantStore, |
|||
IDistributedEventBus distributedEventBus, |
|||
string databaseName) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
TenantStore = tenantStore; |
|||
DatabaseName = databaseName; |
|||
DistributedEventBus = distributedEventBus; |
|||
|
|||
Logger = NullLogger<DatabaseMigrationEventHandlerBase<TDbContext>>.Instance; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Apply pending EF Core schema migrations to the database.
|
|||
/// Returns true if any migration has applied.
|
|||
/// </summary>
|
|||
protected virtual async Task<bool> MigrateDatabaseSchemaAsync(Guid? tenantId) |
|||
{ |
|||
var result = false; |
|||
|
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false)) |
|||
{ |
|||
async Task<bool> MigrateDatabaseSchemaWithDbContextAsync() |
|||
{ |
|||
var dbContext = await uow.ServiceProvider |
|||
.GetRequiredService<IDbContextProvider<TDbContext>>() |
|||
.GetDbContextAsync(); |
|||
|
|||
if ((await dbContext.Database.GetPendingMigrationsAsync()).Any()) |
|||
{ |
|||
await dbContext.Database.MigrateAsync(); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
if (tenantId == null) |
|||
{ |
|||
//Migrating the host database
|
|||
result = await MigrateDatabaseSchemaWithDbContextAsync(); |
|||
} |
|||
else |
|||
{ |
|||
var tenantConfiguration = await TenantStore.FindAsync(tenantId.Value); |
|||
if (!tenantConfiguration.ConnectionStrings.Default.IsNullOrWhiteSpace() || |
|||
!tenantConfiguration.ConnectionStrings.GetOrDefault(DatabaseName).IsNullOrWhiteSpace()) |
|||
{ |
|||
//Migrating the tenant database (only if tenant has a separate database)
|
|||
result = await MigrateDatabaseSchemaWithDbContextAsync(); |
|||
} |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorOnApplyDatabaseMigrationAsync( |
|||
ApplyDatabaseMigrationsEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Log.Warning($"Could not apply database migrations. Re-queueing the operation. TenantId = {eventData.TenantId}, Database Name = {eventData.DatabaseName}."); |
|||
Log.Error(exception.ToString()); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
Log.Warning("Re publishing the event!"); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Log.Error($"Could not apply database migrations. Canceling the operation. TenantId = {eventData.TenantId}, DatabaseName = {eventData.DatabaseName}."); |
|||
Log.Error(exception.ToString()); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorTenantCreatedAsync( |
|||
TenantCreatedEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Logger.LogWarning($"Could not perform tenant created event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception, LogLevel.Warning); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Logger.LogError($"Could not perform tenant created event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorTenantConnectionStringUpdatedAsync( |
|||
TenantConnectionStringUpdatedEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Logger.LogWarning($"Could not perform tenant connection string updated event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception, LogLevel.Warning); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Logger.LogError($"Could not perform tenant connection string updated event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception); |
|||
} |
|||
} |
|||
|
|||
private static int GetEventTryCount(EtoBase eventData) |
|||
{ |
|||
var tryCountAsString = eventData.Properties.GetOrDefault(TryCountPropertyName); |
|||
if (tryCountAsString.IsNullOrEmpty()) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
return int.Parse(tryCountAsString); |
|||
} |
|||
|
|||
private static void SetEventTryCount(EtoBase eventData, int count) |
|||
{ |
|||
eventData.Properties[TryCountPropertyName] = count.ToString(); |
|||
} |
|||
|
|||
private static int IncrementEventTryCount(EtoBase eventData) |
|||
{ |
|||
var count = GetEventTryCount(eventData) + 1; |
|||
SetEventTryCount(eventData, count); |
|||
return count; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,188 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Serilog; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore; |
|||
|
|||
public abstract class DatabaseEfCoreMigrationEventHandler<TDbContext> : DatabaseMigrationEventHandlerBase |
|||
where TDbContext : DbContext, IEfCoreDbContext |
|||
{ |
|||
protected const string TryCountPropertyName = "TryCount"; |
|||
protected const int MaxEventTryCount = 3; |
|||
|
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected ITenantStore TenantStore { get; } |
|||
protected IDistributedEventBus DistributedEventBus { get; } |
|||
protected ILogger<DatabaseEfCoreMigrationEventHandler<TDbContext>> Logger { get; set; } |
|||
protected string DatabaseName { get; } |
|||
|
|||
protected DatabaseEfCoreMigrationEventHandler( |
|||
ICurrentTenant currentTenant, |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
ITenantStore tenantStore, |
|||
IDistributedEventBus distributedEventBus, |
|||
string databaseName) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
TenantStore = tenantStore; |
|||
DatabaseName = databaseName; |
|||
DistributedEventBus = distributedEventBus; |
|||
|
|||
Logger = NullLogger<DatabaseEfCoreMigrationEventHandler<TDbContext>>.Instance; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Apply pending EF Core schema migrations to the database.
|
|||
/// Returns true if any migration has applied.
|
|||
/// </summary>
|
|||
protected virtual async Task<bool> MigrateDatabaseSchemaAsync(Guid? tenantId) |
|||
{ |
|||
var result = false; |
|||
|
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false)) |
|||
{ |
|||
async Task<bool> MigrateDatabaseSchemaWithDbContextAsync() |
|||
{ |
|||
var dbContext = await uow.ServiceProvider |
|||
.GetRequiredService<IDbContextProvider<TDbContext>>() |
|||
.GetDbContextAsync(); |
|||
|
|||
if ((await dbContext.Database.GetPendingMigrationsAsync()).Any()) |
|||
{ |
|||
await dbContext.Database.MigrateAsync(); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
if (tenantId == null) |
|||
{ |
|||
//Migrating the host database
|
|||
result = await MigrateDatabaseSchemaWithDbContextAsync(); |
|||
} |
|||
else |
|||
{ |
|||
var tenantConfiguration = await TenantStore.FindAsync(tenantId.Value); |
|||
if (!tenantConfiguration.ConnectionStrings.Default.IsNullOrWhiteSpace() || |
|||
!tenantConfiguration.ConnectionStrings.GetOrDefault(DatabaseName).IsNullOrWhiteSpace()) |
|||
{ |
|||
//Migrating the tenant database (only if tenant has a separate database)
|
|||
result = await MigrateDatabaseSchemaWithDbContextAsync(); |
|||
} |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorOnApplyDatabaseMigrationAsync( |
|||
ApplyDatabaseMigrationsEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Log.Warning( |
|||
$"Could not apply database migrations. Re-queueing the operation. TenantId = {eventData.TenantId}, Database Name = {eventData.DatabaseName}."); |
|||
Log.Error(exception.ToString()); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
Log.Warning("Re publishing the event!"); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Log.Error( |
|||
$"Could not apply database migrations. Canceling the operation. TenantId = {eventData.TenantId}, DatabaseName = {eventData.DatabaseName}."); |
|||
Log.Error(exception.ToString()); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorTenantCreatedAsync( |
|||
TenantCreatedEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Logger.LogWarning( |
|||
$"Could not perform tenant created event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception, LogLevel.Warning); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Logger.LogError( |
|||
$"Could not perform tenant created event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorTenantConnectionStringUpdatedAsync( |
|||
TenantConnectionStringUpdatedEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Logger.LogWarning( |
|||
$"Could not perform tenant connection string updated event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception, LogLevel.Warning); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Logger.LogError( |
|||
$"Could not perform tenant connection string updated event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception); |
|||
} |
|||
} |
|||
|
|||
private static int GetEventTryCount(EtoBase eventData) |
|||
{ |
|||
var tryCountAsString = eventData.Properties.GetOrDefault(TryCountPropertyName); |
|||
if (tryCountAsString.IsNullOrEmpty()) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
return int.Parse(tryCountAsString); |
|||
} |
|||
|
|||
private static void SetEventTryCount(EtoBase eventData, int count) |
|||
{ |
|||
eventData.Properties[TryCountPropertyName] = count.ToString(); |
|||
} |
|||
|
|||
private static int IncrementEventTryCount(EtoBase eventData) |
|||
{ |
|||
var count = GetEventTryCount(eventData) + 1; |
|||
SetEventTryCount(eventData, count); |
|||
return count; |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore; |
|||
|
|||
public abstract class PendingEfCoreMigrationsChecker<TDbContext> : PendingMigrationsCheckerBase |
|||
where TDbContext : DbContext |
|||
{ |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IDistributedEventBus DistributedEventBus { get; } |
|||
protected string DatabaseName { get; } |
|||
|
|||
protected PendingEfCoreMigrationsChecker( |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IServiceProvider serviceProvider, |
|||
ICurrentTenant currentTenant, |
|||
IDistributedEventBus distributedEventBus, |
|||
string databaseName) |
|||
{ |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
ServiceProvider = serviceProvider; |
|||
CurrentTenant = currentTenant; |
|||
DistributedEventBus = distributedEventBus; |
|||
DatabaseName = databaseName; |
|||
} |
|||
|
|||
public virtual async Task<bool> CheckAsync() |
|||
{ |
|||
var isMigrationRequired = false; |
|||
|
|||
using (CurrentTenant.Change(null)) |
|||
{ |
|||
// Create database tables if needed
|
|||
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false)) |
|||
{ |
|||
var pendingMigrations = await ServiceProvider |
|||
.GetRequiredService<TDbContext>() |
|||
.Database |
|||
.GetPendingMigrationsAsync(); |
|||
|
|||
if (pendingMigrations.Any()) |
|||
{ |
|||
await DistributedEventBus.PublishAsync( |
|||
new ApplyDatabaseMigrationsEto |
|||
{ |
|||
DatabaseName = DatabaseName |
|||
} |
|||
); |
|||
isMigrationRequired = true; |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
return isMigrationRequired; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,204 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using MongoDB.Driver; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.MongoDB; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.MongoDb; |
|||
|
|||
public abstract class DatabaseMongoDbMigrationEventHandler<TDbContext> : DatabaseMigrationEventHandlerBase |
|||
where TDbContext : AbpMongoDbContext, IAbpMongoDbContext |
|||
{ |
|||
protected const string TryCountPropertyName = "TryCount"; |
|||
protected const int MaxEventTryCount = 3; |
|||
|
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected ITenantStore TenantStore { get; } |
|||
protected IDistributedEventBus DistributedEventBus { get; } |
|||
protected ILogger<DatabaseMongoDbMigrationEventHandler<TDbContext>> Logger { get; set; } |
|||
|
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected string DatabaseName { get; } |
|||
|
|||
protected DatabaseMongoDbMigrationEventHandler( |
|||
ICurrentTenant currentTenant, |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
ITenantStore tenantStore, |
|||
IDistributedEventBus distributedEventBus, |
|||
string databaseName, IServiceProvider serviceProvider) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
TenantStore = tenantStore; |
|||
DatabaseName = databaseName; |
|||
ServiceProvider = serviceProvider; |
|||
DistributedEventBus = distributedEventBus; |
|||
|
|||
Logger = NullLogger<DatabaseMongoDbMigrationEventHandler<TDbContext>>.Instance; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Apply pending EF Core schema migrations to the database.
|
|||
/// Returns true if any migration has applied.
|
|||
/// </summary>
|
|||
protected virtual async Task<bool> MigrateDatabaseSchemaAsync(Guid? tenantId) |
|||
{ |
|||
var result = false; |
|||
|
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false)) |
|||
{ |
|||
async Task<bool> MigrateDatabaseSchemaWithDbContextAsync() |
|||
{ |
|||
var dbContexts = ServiceProvider.GetServices<IAbpMongoDbContext>(); |
|||
var connectionStringResolver = ServiceProvider.GetRequiredService<IConnectionStringResolver>(); |
|||
|
|||
foreach (var dbContext in dbContexts) |
|||
{ |
|||
var connectionString = |
|||
await connectionStringResolver.ResolveAsync( |
|||
ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType())); |
|||
if (connectionString.IsNullOrWhiteSpace()) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var mongoUrl = new MongoUrl(connectionString); |
|||
var databaseName = mongoUrl.DatabaseName; |
|||
var client = new MongoClient(mongoUrl); |
|||
|
|||
if (databaseName.IsNullOrWhiteSpace()) |
|||
{ |
|||
databaseName = ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType()); |
|||
} |
|||
|
|||
(dbContext as AbpMongoDbContext)?.InitializeCollections(client.GetDatabase(databaseName)); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
if (tenantId == null) |
|||
{ |
|||
//Migrating the host database
|
|||
result = await MigrateDatabaseSchemaWithDbContextAsync(); |
|||
} |
|||
else |
|||
{ |
|||
var tenantConfiguration = await TenantStore.FindAsync(tenantId.Value); |
|||
if (!tenantConfiguration.ConnectionStrings.Default.IsNullOrWhiteSpace() || |
|||
!tenantConfiguration.ConnectionStrings.GetOrDefault(DatabaseName).IsNullOrWhiteSpace()) |
|||
{ |
|||
//Migrating the tenant database (only if tenant has a separate database)
|
|||
result = await MigrateDatabaseSchemaWithDbContextAsync(); |
|||
} |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorOnApplyDatabaseMigrationAsync( |
|||
ApplyDatabaseMigrationsEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Logger.LogWarning( |
|||
$"Could not apply database migrations. Re-queueing the operation. TenantId = {eventData.TenantId}, Database Name = {eventData.DatabaseName}."); |
|||
Logger.LogException(exception, LogLevel.Warning); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Logger.LogError( |
|||
$"Could not apply database migrations. Canceling the operation. TenantId = {eventData.TenantId}, DatabaseName = {eventData.DatabaseName}."); |
|||
Logger.LogException(exception); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorTenantCreatedAsync( |
|||
TenantCreatedEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Logger.LogWarning( |
|||
$"Could not perform tenant created event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception, LogLevel.Warning); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Logger.LogError( |
|||
$"Could not perform tenant created event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task HandleErrorTenantConnectionStringUpdatedAsync( |
|||
TenantConnectionStringUpdatedEto eventData, |
|||
Exception exception) |
|||
{ |
|||
var tryCount = IncrementEventTryCount(eventData); |
|||
if (tryCount <= MaxEventTryCount) |
|||
{ |
|||
Logger.LogWarning( |
|||
$"Could not perform tenant connection string updated event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception, LogLevel.Warning); |
|||
|
|||
await Task.Delay(RandomHelper.GetRandom(5000, 15000)); |
|||
await DistributedEventBus.PublishAsync(eventData); |
|||
} |
|||
else |
|||
{ |
|||
Logger.LogError( |
|||
$"Could not perform tenant connection string updated event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}."); |
|||
Logger.LogException(exception); |
|||
} |
|||
} |
|||
|
|||
private static int GetEventTryCount(EtoBase eventData) |
|||
{ |
|||
var tryCountAsString = eventData.Properties.GetOrDefault(TryCountPropertyName); |
|||
if (tryCountAsString.IsNullOrEmpty()) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
return int.Parse(tryCountAsString); |
|||
} |
|||
|
|||
private static void SetEventTryCount(EtoBase eventData, int count) |
|||
{ |
|||
eventData.Properties[TryCountPropertyName] = count.ToString(); |
|||
} |
|||
|
|||
private static int IncrementEventTryCount(EtoBase eventData) |
|||
{ |
|||
var count = GetEventTryCount(eventData) + 1; |
|||
SetEventTryCount(eventData, count); |
|||
return count; |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.MongoDB; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations; |
|||
|
|||
public class PendingMongoDbMigrationsChecker<TDbContext> : PendingMigrationsCheckerBase |
|||
where TDbContext : AbpMongoDbContext |
|||
{ |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IDistributedEventBus DistributedEventBus { get; } |
|||
protected string DatabaseName { get; } |
|||
|
|||
protected PendingMongoDbMigrationsChecker( |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IServiceProvider serviceProvider, |
|||
ICurrentTenant currentTenant, |
|||
IDistributedEventBus distributedEventBus, |
|||
string databaseName) |
|||
{ |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
ServiceProvider = serviceProvider; |
|||
CurrentTenant = currentTenant; |
|||
DistributedEventBus = distributedEventBus; |
|||
DatabaseName = databaseName; |
|||
} |
|||
|
|||
public virtual async Task CheckAsync() |
|||
{ |
|||
using (CurrentTenant.Change(null)) |
|||
{ |
|||
// Create database tables if needed
|
|||
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false)) |
|||
{ |
|||
await DistributedEventBus.PublishAsync( |
|||
new ApplyDatabaseMigrationsEto |
|||
{ |
|||
DatabaseName = DatabaseName |
|||
} |
|||
); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,69 +1,8 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations |
|||
{ |
|||
public abstract class PendingMigrationsCheckerBase<TDbContext> : ITransientDependency |
|||
where TDbContext : DbContext |
|||
public abstract class PendingMigrationsCheckerBase : ITransientDependency |
|||
{ |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IDistributedEventBus DistributedEventBus { get; } |
|||
protected string DatabaseName { get; } |
|||
|
|||
protected PendingMigrationsCheckerBase( |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
IServiceProvider serviceProvider, |
|||
ICurrentTenant currentTenant, |
|||
IDistributedEventBus distributedEventBus, |
|||
string databaseName) |
|||
{ |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
ServiceProvider = serviceProvider; |
|||
CurrentTenant = currentTenant; |
|||
DistributedEventBus = distributedEventBus; |
|||
DatabaseName = databaseName; |
|||
} |
|||
|
|||
public virtual async Task<bool> CheckAsync() |
|||
{ |
|||
var isMigrationRequired = false; |
|||
|
|||
using (CurrentTenant.Change(null)) |
|||
{ |
|||
// Create database tables if needed
|
|||
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false)) |
|||
{ |
|||
var pendingMigrations = await ServiceProvider |
|||
.GetRequiredService<TDbContext>() |
|||
.Database |
|||
.GetPendingMigrationsAsync(); |
|||
|
|||
if (pendingMigrations.Any()) |
|||
{ |
|||
await DistributedEventBus.PublishAsync( |
|||
new ApplyDatabaseMigrationsEto |
|||
{ |
|||
DatabaseName = DatabaseName |
|||
} |
|||
); |
|||
isMigrationRequired = true; |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
return isMigrationRequired; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue