diff --git a/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs index ac66f35864..60826a14dd 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs @@ -7,32 +7,36 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionRepositoryExtensions { - public static IServiceCollection AddDefaultRepository(this IServiceCollection services, Type entityType, Type repositoryImplementationType) + public static IServiceCollection AddDefaultRepository( + this IServiceCollection services, + Type entityType, + Type repositoryImplementationType, + bool replaceExisting = false) { //IReadOnlyBasicRepository var readOnlyBasicRepositoryInterface = typeof(IReadOnlyBasicRepository<>).MakeGenericType(entityType); if (readOnlyBasicRepositoryInterface.IsAssignableFrom(repositoryImplementationType)) { - services.TryAddTransient(readOnlyBasicRepositoryInterface, repositoryImplementationType); + RegisterService(services, readOnlyBasicRepositoryInterface, repositoryImplementationType, replaceExisting); //IReadOnlyRepository var readOnlyRepositoryInterface = typeof(IReadOnlyRepository<>).MakeGenericType(entityType); if (readOnlyRepositoryInterface.IsAssignableFrom(repositoryImplementationType)) { - services.TryAddTransient(readOnlyRepositoryInterface, repositoryImplementationType); + RegisterService(services, readOnlyRepositoryInterface, repositoryImplementationType, replaceExisting); } //IBasicRepository var basicRepositoryInterface = typeof(IBasicRepository<>).MakeGenericType(entityType); if (basicRepositoryInterface.IsAssignableFrom(repositoryImplementationType)) { - services.TryAddTransient(basicRepositoryInterface, repositoryImplementationType); + RegisterService(services, basicRepositoryInterface, repositoryImplementationType, replaceExisting); //IRepository var repositoryInterface = typeof(IRepository<>).MakeGenericType(entityType); if (repositoryInterface.IsAssignableFrom(repositoryImplementationType)) { - services.TryAddTransient(repositoryInterface, repositoryImplementationType); + RegisterService(services, repositoryInterface, repositoryImplementationType, replaceExisting); } } } @@ -44,26 +48,26 @@ namespace Microsoft.Extensions.DependencyInjection var readOnlyBasicRepositoryInterfaceWithPk = typeof(IReadOnlyBasicRepository<,>).MakeGenericType(entityType, primaryKeyType); if (readOnlyBasicRepositoryInterfaceWithPk.IsAssignableFrom(repositoryImplementationType)) { - services.TryAddTransient(readOnlyBasicRepositoryInterfaceWithPk, repositoryImplementationType); + RegisterService(services, readOnlyBasicRepositoryInterfaceWithPk, repositoryImplementationType, replaceExisting); //IReadOnlyRepository var readOnlyRepositoryInterfaceWithPk = typeof(IReadOnlyRepository<,>).MakeGenericType(entityType, primaryKeyType); if (readOnlyRepositoryInterfaceWithPk.IsAssignableFrom(repositoryImplementationType)) { - services.TryAddTransient(readOnlyRepositoryInterfaceWithPk, repositoryImplementationType); + RegisterService(services, readOnlyRepositoryInterfaceWithPk, repositoryImplementationType, replaceExisting); } //IBasicRepository var basicRepositoryInterfaceWithPk = typeof(IBasicRepository<,>).MakeGenericType(entityType, primaryKeyType); if (basicRepositoryInterfaceWithPk.IsAssignableFrom(repositoryImplementationType)) { - services.TryAddTransient(basicRepositoryInterfaceWithPk, repositoryImplementationType); + RegisterService(services, basicRepositoryInterfaceWithPk, repositoryImplementationType, replaceExisting); //IRepository var repositoryInterfaceWithPk = typeof(IRepository<,>).MakeGenericType(entityType, primaryKeyType); if (repositoryInterfaceWithPk.IsAssignableFrom(repositoryImplementationType)) { - services.TryAddTransient(repositoryInterfaceWithPk, repositoryImplementationType); + RegisterService(services, repositoryInterfaceWithPk, repositoryImplementationType, replaceExisting); } } } @@ -71,5 +75,21 @@ namespace Microsoft.Extensions.DependencyInjection return services; } + + private static void RegisterService( + IServiceCollection services, + Type serviceType, + Type implementationType, + bool replaceExisting) + { + if (replaceExisting) + { + services.Replace(ServiceDescriptor.Transient(serviceType, implementationType)); + } + else + { + services.TryAddTransient(serviceType, implementationType); + } + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/AbpRepositoryConventionalRegistrar.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/AbpRepositoryConventionalRegistrar.cs index f11812f2ea..f7b9527aa0 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/AbpRepositoryConventionalRegistrar.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/AbpRepositoryConventionalRegistrar.cs @@ -6,6 +6,9 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Domain.Repositories { + /* Repositories are not injected by class by default. + * This class specializes repository registration to apply this rule. + */ public class AbpRepositoryConventionalRegistrar : DefaultConventionalRegistrar { public static bool ExposeRepositoryClasses { get; set; } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs index f3b2fc388f..548a636eff 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryRegistrarBase.cs @@ -20,7 +20,7 @@ namespace Volo.Abp.Domain.Repositories { foreach (var customRepository in Options.CustomRepositories) { - Options.Services.AddDefaultRepository(customRepository.Key, customRepository.Value); + Options.Services.AddDefaultRepository(customRepository.Key, customRepository.Value, replaceExisting: true); } if (Options.RegisterDefaultRepositories) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs index d39c5db121..538614ee22 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs @@ -21,7 +21,17 @@ namespace Microsoft.Extensions.DependencyInjection foreach (var dbContextType in options.ReplacedDbContextTypes) { - services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TDbContext))); + services.Replace( + ServiceDescriptor.Transient( + dbContextType, + sp => sp.GetRequiredService(typeof(TDbContext)) + ) + ); + + services.Configure(opts => + { + opts.DbContextReplacements[dbContextType] = typeof(TDbContext); + }); } new EfCoreRepositoryRegistrar(options).AddRepositories(); diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Properties/AssemblyInfo.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Properties/AssemblyInfo.cs index 9345c565d8..9d3bf27070 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Properties/AssemblyInfo.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -8,6 +9,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Volo.Abp.EntityFrameworkCore")] [assembly: AssemblyTrademark("")] +[assembly: InternalsVisibleTo("Volo.Abp.EntityFrameworkCore.Tests")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs index b1b84f7236..92c6935900 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs @@ -7,19 +7,22 @@ namespace Volo.Abp.EntityFrameworkCore { public class AbpDbContextOptions { - internal List> DefaultPreConfigureActions { get; set; } + internal List> DefaultPreConfigureActions { get; } internal Action DefaultConfigureAction { get; set; } - internal Dictionary> PreConfigureActions { get; set; } + internal Dictionary> PreConfigureActions { get; } - internal Dictionary ConfigureActions { get; set; } + internal Dictionary ConfigureActions { get; } + + internal Dictionary DbContextReplacements { get; } public AbpDbContextOptions() { DefaultPreConfigureActions = new List>(); PreConfigureActions = new Dictionary>(); ConfigureActions = new Dictionary(); + DbContextReplacements = new Dictionary(); } public void PreConfigure([NotNull] Action action) @@ -57,5 +60,29 @@ namespace Volo.Abp.EntityFrameworkCore ConfigureActions[typeof(TDbContext)] = action; } + + internal Type GetReplacedTypeOrSelf(Type dbContextType) + { + var replacementType = dbContextType; + while (true) + { + if (DbContextReplacements.TryGetValue(replacementType, out var foundType)) + { + if (foundType == dbContextType) + { + throw new AbpException( + "Circular DbContext replacement found for " + + dbContextType.AssemblyQualifiedName + ); + } + + replacementType = foundType; + } + else + { + return replacementType; + } + } + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreDatabaseApi.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreDatabaseApi.cs index cffda462b0..151d78e255 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreDatabaseApi.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreDatabaseApi.cs @@ -4,12 +4,11 @@ using Volo.Abp.EntityFrameworkCore; namespace Volo.Abp.Uow.EntityFrameworkCore { - public class EfCoreDatabaseApi : IDatabaseApi, ISupportsSavingChanges - where TDbContext : IEfCoreDbContext + public class EfCoreDatabaseApi : IDatabaseApi, ISupportsSavingChanges { - public TDbContext DbContext { get; } + public IEfCoreDbContext DbContext { get; } - public EfCoreDatabaseApi(TDbContext dbContext) + public EfCoreDatabaseApi(IEfCoreDbContext dbContext) { DbContext = dbContext; } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs index 89cc69fb74..07f1d200aa 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.DependencyInjection; @@ -14,8 +15,6 @@ using Volo.Abp.Threading; namespace Volo.Abp.Uow.EntityFrameworkCore { - //TODO: Implement logic in DefaultDbContextResolver.Resolve in old ABP. - public class UnitOfWorkDbContextProvider : IDbContextProvider where TDbContext : IEfCoreDbContext { @@ -25,17 +24,20 @@ namespace Volo.Abp.Uow.EntityFrameworkCore private readonly IConnectionStringResolver _connectionStringResolver; private readonly ICancellationTokenProvider _cancellationTokenProvider; private readonly ICurrentTenant _currentTenant; + private readonly AbpDbContextOptions _options; public UnitOfWorkDbContextProvider( IUnitOfWorkManager unitOfWorkManager, IConnectionStringResolver connectionStringResolver, ICancellationTokenProvider cancellationTokenProvider, - ICurrentTenant currentTenant) + ICurrentTenant currentTenant, + IOptions options) { _unitOfWorkManager = unitOfWorkManager; _connectionStringResolver = connectionStringResolver; _cancellationTokenProvider = cancellationTokenProvider; _currentTenant = currentTenant; + _options = options.Value; Logger = NullLogger>.Instance; } @@ -63,15 +65,16 @@ namespace Volo.Abp.Uow.EntityFrameworkCore var connectionStringName = ConnectionStringNameAttribute.GetConnStringName(); var connectionString = ResolveConnectionString(connectionStringName); - var dbContextKey = $"{typeof(TDbContext).FullName}_{connectionString}"; + var targetDbContextType = _options.GetReplacedTypeOrSelf(typeof(TDbContext)); + var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}"; var databaseApi = unitOfWork.GetOrAddDatabaseApi( dbContextKey, - () => new EfCoreDatabaseApi( + () => new EfCoreDatabaseApi( CreateDbContext(unitOfWork, connectionStringName, connectionString) )); - return ((EfCoreDatabaseApi)databaseApi).DbContext; + return (TDbContext)((EfCoreDatabaseApi)databaseApi).DbContext; } public async Task GetDbContextAsync() @@ -85,20 +88,21 @@ namespace Volo.Abp.Uow.EntityFrameworkCore var connectionStringName = ConnectionStringNameAttribute.GetConnStringName(); var connectionString = await ResolveConnectionStringAsync(connectionStringName); - var dbContextKey = $"{typeof(TDbContext).FullName}_{connectionString}"; + var targetDbContextType = _options.GetReplacedTypeOrSelf(typeof(TDbContext)); + var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}"; var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey); if (databaseApi == null) { - databaseApi = new EfCoreDatabaseApi( + databaseApi = new EfCoreDatabaseApi( await CreateDbContextAsync(unitOfWork, connectionStringName, connectionString) ); unitOfWork.AddDatabaseApi(dbContextKey, databaseApi); } - return ((EfCoreDatabaseApi)databaseApi).DbContext; + return (TDbContext)((EfCoreDatabaseApi)databaseApi).DbContext; } private TDbContext CreateDbContext(IUnitOfWork unitOfWork, string connectionStringName, string connectionString) diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/AbpMemoryDbModule.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/AbpMemoryDbModule.cs index bd8b4b98a4..5452abba03 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/AbpMemoryDbModule.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/AbpMemoryDbModule.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Domain; using Volo.Abp.Domain.Repositories.MemoryDb; -using Volo.Abp.Json; using Volo.Abp.Modularity; using Volo.Abp.Uow.MemoryDb; diff --git a/framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs b/framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs index 1ad31d2de9..5adf7300ee 100644 --- a/framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs @@ -17,6 +17,21 @@ namespace Microsoft.Extensions.DependencyInjection { services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TMongoDbContext))); } + + foreach (var dbContextType in options.ReplacedDbContextTypes) + { + services.Replace( + ServiceDescriptor.Transient( + dbContextType, + sp => sp.GetRequiredService(typeof(TMongoDbContext)) + ) + ); + + services.Configure(opts => + { + opts.DbContextReplacements[dbContextType] = typeof(TMongoDbContext); + }); + } new MongoDbRepositoryRegistrar(options).AddRepositories(); diff --git a/framework/src/Volo.Abp.MongoDB/Properties/AssemblyInfo.cs b/framework/src/Volo.Abp.MongoDB/Properties/AssemblyInfo.cs index 51745424f2..77656ec064 100644 --- a/framework/src/Volo.Abp.MongoDB/Properties/AssemblyInfo.cs +++ b/framework/src/Volo.Abp.MongoDB/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -8,6 +9,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Volo.Abp.MongoDB")] [assembly: AssemblyTrademark("")] +[assembly: InternalsVisibleTo("Volo.Abp.MongoDB.Tests")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContextOptions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContextOptions.cs new file mode 100644 index 0000000000..aaee9571cd --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContextOptions.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.MongoDB +{ + public class AbpMongoDbContextOptions + { + internal Dictionary DbContextReplacements { get; } + + public AbpMongoDbContextOptions() + { + DbContextReplacements = new Dictionary(); + } + + internal Type GetReplacedTypeOrSelf(Type dbContextType) + { + var replacementType = dbContextType; + while (true) + { + if (DbContextReplacements.TryGetValue(replacementType, out var foundType)) + { + if (foundType == dbContextType) + { + throw new AbpException( + "Circular DbContext replacement found for " + + dbContextType.AssemblyQualifiedName + ); + } + + replacementType = foundType; + } + else + { + return replacementType; + } + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/MongoDbDatabaseApi.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/MongoDbDatabaseApi.cs index bb02ad0108..c1beda98aa 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/MongoDbDatabaseApi.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/MongoDbDatabaseApi.cs @@ -1,10 +1,12 @@ -namespace Volo.Abp.Uow.MongoDB +using Volo.Abp.MongoDB; + +namespace Volo.Abp.Uow.MongoDB { - public class MongoDbDatabaseApi : IDatabaseApi + public class MongoDbDatabaseApi : IDatabaseApi { - public TMongoDbContext DbContext { get; } + public IAbpMongoDbContext DbContext { get; } - public MongoDbDatabaseApi(TMongoDbContext dbContext) + public MongoDbDatabaseApi(IAbpMongoDbContext dbContext) { DbContext = dbContext; } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs index 684593fb7f..a26307234d 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; using Volo.Abp.Data; @@ -22,17 +23,20 @@ namespace Volo.Abp.Uow.MongoDB private readonly IConnectionStringResolver _connectionStringResolver; private readonly ICancellationTokenProvider _cancellationTokenProvider; private readonly ICurrentTenant _currentTenant; + private readonly AbpMongoDbContextOptions _options; public UnitOfWorkMongoDbContextProvider( IUnitOfWorkManager unitOfWorkManager, IConnectionStringResolver connectionStringResolver, ICancellationTokenProvider cancellationTokenProvider, - ICurrentTenant currentTenant) + ICurrentTenant currentTenant, + IOptions options) { _unitOfWorkManager = unitOfWorkManager; _connectionStringResolver = connectionStringResolver; _cancellationTokenProvider = cancellationTokenProvider; _currentTenant = currentTenant; + _options = options.Value; Logger = NullLogger>.Instance; } @@ -59,7 +63,8 @@ namespace Volo.Abp.Uow.MongoDB } var connectionString = ResolveConnectionString(); - var dbContextKey = $"{typeof(TMongoDbContext).FullName}_{connectionString}"; + var targetDbContextType = _options.GetReplacedTypeOrSelf(typeof(TMongoDbContext)); + var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}"; var mongoUrl = new MongoUrl(connectionString); var databaseName = mongoUrl.DatabaseName; @@ -71,9 +76,9 @@ namespace Volo.Abp.Uow.MongoDB //TODO: Create only single MongoDbClient per connection string in an application (extract MongoClientCache for example). var databaseApi = unitOfWork.GetOrAddDatabaseApi( dbContextKey, - () => new MongoDbDatabaseApi(CreateDbContext(unitOfWork, mongoUrl, databaseName))); + () => new MongoDbDatabaseApi(CreateDbContext(unitOfWork, mongoUrl, databaseName))); - return ((MongoDbDatabaseApi) databaseApi).DbContext; + return (TMongoDbContext)((MongoDbDatabaseApi) databaseApi).DbContext; } public async Task GetDbContextAsync(CancellationToken cancellationToken = default) @@ -86,7 +91,8 @@ namespace Volo.Abp.Uow.MongoDB } var connectionString = await ResolveConnectionStringAsync(); - var dbContextKey = $"{typeof(TMongoDbContext).FullName}_{connectionString}"; + var targetDbContextType = _options.GetReplacedTypeOrSelf(typeof(TMongoDbContext)); + var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}"; var mongoUrl = new MongoUrl(connectionString); var databaseName = mongoUrl.DatabaseName; @@ -99,7 +105,7 @@ namespace Volo.Abp.Uow.MongoDB var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey); if (databaseApi == null) { - databaseApi = new MongoDbDatabaseApi( + databaseApi = new MongoDbDatabaseApi( await CreateDbContextAsync( unitOfWork, mongoUrl, @@ -111,7 +117,7 @@ namespace Volo.Abp.Uow.MongoDB unitOfWork.AddDatabaseApi(dbContextKey, databaseApi); } - return ((MongoDbDatabaseApi) databaseApi).DbContext; + return (TMongoDbContext)((MongoDbDatabaseApi) databaseApi).DbContext; } [Obsolete("Use CreateDbContextAsync")] diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DbContext_Replace_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DbContext_Replace_Tests.cs index 977cb8522a..8fd88f23fe 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DbContext_Replace_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DbContext_Replace_Tests.cs @@ -1,9 +1,11 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Shouldly; using Volo.Abp.Domain.Repositories; using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext; +using Volo.Abp.TestApp.Domain; using Volo.Abp.TestApp.EntityFrameworkCore; using Volo.Abp.Uow; using Xunit; @@ -13,24 +15,40 @@ namespace Volo.Abp.EntityFrameworkCore public class DbContext_Replace_Tests : EntityFrameworkCoreTestBase { private readonly IBasicRepository _dummyRepository; + private readonly IPersonRepository _personRepository; private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly AbpDbContextOptions _options; public DbContext_Replace_Tests() { - _dummyRepository = ServiceProvider.GetRequiredService>(); - _unitOfWorkManager = ServiceProvider.GetRequiredService(); + _dummyRepository = GetRequiredService>(); + _personRepository = GetRequiredService(); + _unitOfWorkManager = GetRequiredService(); + _options = GetRequiredService>().Value; } [Fact] public async Task Should_Replace_DbContext() { + _options.GetReplacedTypeOrSelf(typeof(IThirdDbContext)).ShouldBe(typeof(TestAppDbContext)); + (ServiceProvider.GetRequiredService() is TestAppDbContext).ShouldBeTrue(); using (var uow = _unitOfWorkManager.Begin()) { - ((await _dummyRepository.GetDbContextAsync()) is IThirdDbContext).ShouldBeTrue(); - ((await _dummyRepository.GetDbContextAsync()) is TestAppDbContext).ShouldBeTrue(); + var instance1 = await _dummyRepository.GetDbContextAsync(); + (instance1 is IThirdDbContext).ShouldBeTrue(); + var instance2 = await _dummyRepository.GetDbContextAsync(); + (instance2 is TestAppDbContext).ShouldBeTrue(); + + var instance3 = await _personRepository.GetDbContextAsync(); + (instance3 is TestAppDbContext).ShouldBeTrue(); + + // All instances should be the same! + instance3.ShouldBe(instance1); + instance3.ShouldBe(instance2); + await uow.CompleteAsync(); } } diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TestAppMongoDbContext.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TestAppMongoDbContext.cs index 03869ce566..eed8d4ade3 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TestAppMongoDbContext.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TestAppMongoDbContext.cs @@ -15,7 +15,7 @@ namespace Volo.Abp.TestApp.MongoDB public IMongoCollection Cities => Collection(); - protected override void CreateModel(IMongoModelBuilder modelBuilder) + protected internal override void CreateModel(IMongoModelBuilder modelBuilder) { base.CreateModel(modelBuilder); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateUpdateBlogPostDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateBlogPostDto.cs similarity index 94% rename from modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateUpdateBlogPostDto.cs rename to modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateBlogPostDto.cs index 4c0532129d..a8423a7300 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateUpdateBlogPostDto.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateBlogPostDto.cs @@ -5,7 +5,7 @@ using Volo.CmsKit.Blogs; namespace Volo.CmsKit.Admin.Blogs { - public class CreateUpdateBlogPostDto + public class CreateBlogPostDto { [Required] public Guid BlogId { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs index 28d614d289..aa110fe938 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs @@ -11,7 +11,8 @@ namespace Volo.CmsKit.Admin.Blogs BlogPostDto, Guid, PagedAndSortedResultRequestDto, - CreateUpdateBlogPostDto> + CreateBlogPostDto, + UpdateBlogPostDto> { Task GetBySlugAsync(string blogSlug, string slug); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/UpdateBlogPostDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/UpdateBlogPostDto.cs new file mode 100644 index 0000000000..909d8e2e9e --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/UpdateBlogPostDto.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; +using Volo.CmsKit.Blogs; + +namespace Volo.CmsKit.Admin.Blogs +{ + public class UpdateBlogPostDto + { + [Required] + [DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxTitleLength))] + public string Title { get; set; } + + [Required] + [DynamicStringLength( + typeof(BlogPostConsts), + nameof(BlogPostConsts.MaxSlugLength), + nameof(BlogPostConsts.MinSlugLength))] + public string Slug { get; set; } + + [DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxShortDescriptionLength))] + public string ShortDescription { get; set; } + + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs index 8fa6fb9ff7..d2c1114dfb 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs @@ -23,7 +23,8 @@ namespace Volo.CmsKit.Admin.Blogs BlogPostDto, Guid, PagedAndSortedResultRequestDto, - CreateUpdateBlogPostDto> + CreateBlogPostDto, + UpdateBlogPostDto> , IBlogPostAdminAppService { protected readonly IBlogPostManager BlogPostManager; @@ -63,7 +64,7 @@ namespace Volo.CmsKit.Admin.Blogs } [Authorize(CmsKitAdminPermissions.BlogPosts.Create)] - public override async Task CreateAsync(CreateUpdateBlogPostDto input) + public override async Task CreateAsync(CreateBlogPostDto input) { _ = await UserLookupService.GetByIdAsync(CurrentUser.GetId()); @@ -80,7 +81,7 @@ namespace Volo.CmsKit.Admin.Blogs } [Authorize(CmsKitAdminPermissions.BlogPosts.Update)] - public override async Task UpdateAsync(Guid id, CreateUpdateBlogPostDto input) + public override async Task UpdateAsync(Guid id, UpdateBlogPostDto input) { var blogPost = await BlogPostRepository.GetAsync(id); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs index 33a9d9de84..173897f0a4 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs @@ -24,7 +24,8 @@ namespace Volo.CmsKit.Admin CreateMap(MemberList.Source); CreateMap(MemberList.Destination); - CreateMap(MemberList.Source); + CreateMap(MemberList.Source); + CreateMap(MemberList.Source); CreateMap(MemberList.Destination) .ReverseMap(); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs index 6c7e78de50..92d23dff4d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs @@ -31,7 +31,7 @@ namespace Volo.CmsKit.Admin.Blogs [HttpPost] [Authorize(CmsKitAdminPermissions.BlogPosts.Create)] - public virtual Task CreateAsync(CreateUpdateBlogPostDto input) + public virtual Task CreateAsync(CreateBlogPostDto input) { return BlogPostAdminAppService.CreateAsync(input); } @@ -107,7 +107,7 @@ namespace Volo.CmsKit.Admin.Blogs [HttpPut] [Route("{id}")] [Authorize(CmsKitAdminPermissions.BlogPosts.Update)] - public virtual Task UpdateAsync(Guid id, CreateUpdateBlogPostDto input) + public virtual Task UpdateAsync(Guid id, UpdateBlogPostDto input) { return BlogPostAdminAppService.UpdateAsync(id, input); } diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostAdminAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostAdminAppService_Tests.cs index efef70cb9b..6516e3fc2c 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostAdminAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostAdminAppService_Tests.cs @@ -31,7 +31,7 @@ namespace Volo.CmsKit.Blogs var slug = "my-awesome-new-post"; var shortDescription = "This blog is all about awesomeness 🤗!"; - var created = await blogPostAdminAppService.CreateAsync(new CreateUpdateBlogPostDto + var created = await blogPostAdminAppService.CreateAsync(new CreateBlogPostDto { BlogId = cmsKitTestData.Blog_Id, Title = title, @@ -55,7 +55,7 @@ namespace Volo.CmsKit.Blogs var slug = "another-my-awesome-new-post"; var shortDescription = "This blog is all about awesomeness 🤗!"; - var dto = new CreateUpdateBlogPostDto + var dto = new CreateBlogPostDto { // Non-existing Id BlogId = Guid.NewGuid(), @@ -137,9 +137,8 @@ namespace Volo.CmsKit.Blogs var title = "[Solved] Another Blog Post"; var slug = "another-short-blog-post"; - await blogPostAdminAppService.UpdateAsync(cmsKitTestData.BlogPost_2_Id, new CreateUpdateBlogPostDto + await blogPostAdminAppService.UpdateAsync(cmsKitTestData.BlogPost_2_Id, new UpdateBlogPostDto { - BlogId = cmsKitTestData.Blog_Id, ShortDescription = shortDescription, Title = title, Slug = slug, @@ -152,31 +151,11 @@ namespace Volo.CmsKit.Blogs blogPost.Slug.ShouldBe(slug); } - [Fact] - public async Task UpdateAsync_ShouldThrowException_WhileChangingWithNonExistingBlogId() - { - var nonExistingId = Guid.NewGuid(); - - var dto = new CreateUpdateBlogPostDto - { - BlogId = nonExistingId, - Title = cmsKitTestData.Page_2_Title, - Slug = cmsKitTestData.BlogPost_2_Slug - }; - - var exception = await Should.ThrowAsync(async () => - await blogPostAdminAppService.UpdateAsync(cmsKitTestData.BlogPost_2_Id, dto)); - - exception.EntityType.ShouldBe(typeof(Blog)); - exception.Id.ShouldBe(nonExistingId); - } - [Fact] public async Task UpdateAsync_ShouldThrowException_WhileUpdatingWithAlreadyExistingSlug() { - var dto = new CreateUpdateBlogPostDto + var dto = new UpdateBlogPostDto { - BlogId = cmsKitTestData.Blog_Id, Title = "Some new title", Slug = cmsKitTestData.BlogPost_1_Slug };