Browse Source

Make `ReplaceDbContext` supports multi-tenancy.

pull/15335/head
maliming 4 years ago
parent
commit
e99032daf2
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 17
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/AbpCommonDbContextRegistrationOptions.cs
  2. 34
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/DbContextTypeWithTenandside.cs
  3. 10
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/IAbpCommonDbContextRegistrationOptionsBuilder.cs
  4. 11
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/ReplaceDbContextAttribute.cs
  5. 23
      framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs
  6. 17
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs
  7. 15
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreDbContextTypeProvider.cs
  8. 3
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IEfCoreDbContextTypeProvider.cs
  9. 69
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs
  10. 2
      framework/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs
  11. 18
      framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs
  12. 17
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContextOptions.cs
  13. 3
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoDbContextTypeProvider.cs
  14. 15
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbContextTypeProvider.cs
  15. 70
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs
  16. 15
      framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContext.cs
  17. 9
      framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContextDummyEntity.cs
  18. 20
      framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContextDummyEntityRepository.cs
  19. 12
      framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContextMultiTenantDummyEntity.cs
  20. 20
      framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContextMultiTenantDummyEntityRepository.cs
  21. 10
      framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/IFifthDbContext.cs
  22. 13
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs
  23. 49
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DbContext_Replace_Tests.cs
  24. 16
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/HostTestAppDbContext.cs
  25. 19
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TenantTestAppDbContext.cs
  26. 10
      framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContext.cs
  27. 9
      framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContextDummyEntity.cs
  28. 26
      framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContextDummyEntityRepository.cs
  29. 12
      framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContextMultiTenantDummyEntity.cs
  30. 26
      framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContextMultiTenantDummyEntityRepository.cs
  31. 10
      framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/IFifthDbContext.cs
  32. 14
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs
  33. 52
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DbContext_Replace_Tests.cs
  34. 12
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/HostTestAppDbContext.cs
  35. 15
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TenantTestAppDbContext.cs

17
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/AbpCommonDbContextRegistrationOptions.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.DependencyInjection;
@ -15,7 +16,7 @@ public abstract class AbpCommonDbContextRegistrationOptions : IAbpCommonDbContex
public IServiceCollection Services { get; }
public Dictionary<Type, Type> ReplacedDbContextTypes { get; }
public Dictionary<MultiTenantDbContextType, Type> ReplacedDbContextTypes { get; }
public Type DefaultRepositoryDbContextType { get; protected set; }
@ -39,28 +40,28 @@ public abstract class AbpCommonDbContextRegistrationOptions : IAbpCommonDbContex
Services = services;
DefaultRepositoryDbContextType = originalDbContextType;
CustomRepositories = new Dictionary<Type, Type>();
ReplacedDbContextTypes = new Dictionary<Type, Type>();
ReplacedDbContextTypes = new Dictionary<MultiTenantDbContextType, Type>();
SpecifiedDefaultRepositories = new List<Type>();
}
public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext>()
public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext>(MultiTenancySides multiTenancySides = MultiTenancySides.Both)
{
return ReplaceDbContext(typeof(TOtherDbContext));
return ReplaceDbContext(typeof(TOtherDbContext), multiTenancySides: multiTenancySides);
}
public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext, TTargetDbContext>()
public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext, TTargetDbContext>(MultiTenancySides multiTenancySides = MultiTenancySides.Both)
{
return ReplaceDbContext(typeof(TOtherDbContext), typeof(TTargetDbContext));
return ReplaceDbContext(typeof(TOtherDbContext), typeof(TTargetDbContext), multiTenancySides);
}
public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType, Type targetDbContextType = null)
public IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType, Type targetDbContextType = null, MultiTenancySides multiTenancySides = MultiTenancySides.Both)
{
if (!otherDbContextType.IsAssignableFrom(OriginalDbContextType))
{
throw new AbpException($"{OriginalDbContextType.AssemblyQualifiedName} should inherit/implement {otherDbContextType.AssemblyQualifiedName}!");
}
ReplacedDbContextTypes[otherDbContextType] = targetDbContextType;
ReplacedDbContextTypes[new MultiTenantDbContextType(otherDbContextType, multiTenancySides)] = targetDbContextType;
return this;
}

34
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/DbContextTypeWithTenandside.cs

@ -0,0 +1,34 @@
using System;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.DependencyInjection;
public class MultiTenantDbContextType
{
public Type Type { get; }
public MultiTenancySides MultiTenancySide { get; }
public MultiTenantDbContextType(Type type, MultiTenancySides multiTenancySide = MultiTenancySides.Both)
{
Type = type;
MultiTenancySide = multiTenancySide;
}
public override bool Equals(object obj)
{
var other = obj as MultiTenantDbContextType;
if (other == null)
{
return false;
}
return other.Type == Type && other.MultiTenancySide == MultiTenancySide;
}
public override int GetHashCode()
{
return Type.GetHashCode() ^ MultiTenancySide.GetHashCode();
}
}

10
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/IAbpCommonDbContextRegistrationOptionsBuilder.cs

@ -1,6 +1,7 @@
using System;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.DependencyInjection;
@ -72,19 +73,22 @@ public interface IAbpCommonDbContextRegistrationOptionsBuilder
/// Replaces given DbContext type with this DbContext type.
/// </summary>
/// <typeparam name="TOtherDbContext">The DbContext type to be replaced</typeparam>
IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext>();
/// <param name="multiTenancySides">MultiTenancy side</param>
IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext>(MultiTenancySides multiTenancySides = MultiTenancySides.Both);
/// <summary>
/// Replaces given DbContext type with the target DbContext type.
/// </summary>
/// <typeparam name="TOtherDbContext">The DbContext type to be replaced</typeparam>
/// <typeparam name="TTargetDbContext">The target DbContext type</typeparam>
IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext, TTargetDbContext>();
/// <param name="multiTenancySides">MultiTenancy side</param>
IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext, TTargetDbContext>(MultiTenancySides multiTenancySides = MultiTenancySides.Both);
/// <summary>
/// Replaces given DbContext type with the given or this DbContext type.
/// </summary>
/// <param name="otherDbContextType">The DbContext type to be replaced</param>
/// <param name="targetDbContextType">The target DbContext type (optional, used this DbContext type if not provided)</param>
IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType, Type targetDbContextType = null);
/// <param name="multiTenancySides">MultiTenancy side</param>
IAbpCommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType, Type targetDbContextType = null, MultiTenancySides multiTenancySides = MultiTenancySides.Both);
}

11
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/DependencyInjection/ReplaceDbContextAttribute.cs

@ -1,14 +1,21 @@
using System;
using System.Linq;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.DependencyInjection;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ReplaceDbContextAttribute : Attribute
{
public Type[] ReplacedDbContextTypes { get; }
public MultiTenantDbContextType[] ReplacedDbContextTypes { get; }
public ReplaceDbContextAttribute(params Type[] replacedDbContextTypes)
{
ReplacedDbContextTypes = replacedDbContextTypes;
ReplacedDbContextTypes = replacedDbContextTypes.Select(type => new MultiTenantDbContextType(type)).ToArray();
}
public ReplaceDbContextAttribute(Type replacedDbContextType, MultiTenancySides side)
{
ReplacedDbContextTypes = new[] {new MultiTenantDbContextType(replacedDbContextType, side)};
}
}

23
framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs

@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Microsoft.Extensions.DependencyInjection;
@ -19,12 +20,12 @@ public static class AbpEfCoreServiceCollectionExtensions
var options = new AbpDbContextRegistrationOptions(typeof(TDbContext), services);
var replacedDbContextTypes = typeof(TDbContext).GetCustomAttributes<ReplaceDbContextAttribute>(true)
var replacedMultiTenantDbContextTypes = typeof(TDbContext).GetCustomAttributes<ReplaceDbContextAttribute>(true)
.SelectMany(x => x.ReplacedDbContextTypes).ToList();
foreach (var dbContextType in replacedDbContextTypes)
foreach (var dbContextType in replacedMultiTenantDbContextTypes)
{
options.ReplaceDbContext(dbContextType);
options.ReplaceDbContext(dbContextType.Type, multiTenancySides: dbContextType.MultiTenancySide);
}
optionsBuilder?.Invoke(options);
@ -33,19 +34,19 @@ public static class AbpEfCoreServiceCollectionExtensions
foreach (var entry in options.ReplacedDbContextTypes)
{
var originalDbContextType = entry.Key;
var originalDbContextType = entry.Key.Type;
var targetDbContextType = entry.Value ?? typeof(TDbContext);
services.Replace(
ServiceDescriptor.Transient(
originalDbContextType,
sp => sp.GetRequiredService(targetDbContextType)
)
);
services.Replace(ServiceDescriptor.Transient(originalDbContextType, sp =>
{
var dbContextType = sp.GetRequiredService<IEfCoreDbContextTypeProvider>().GetDbContextType(originalDbContextType);
return sp.GetRequiredService(dbContextType);
}));
services.Configure<AbpDbContextOptions>(opts =>
{
opts.DbContextReplacements[originalDbContextType] = targetDbContextType;
var multiTenantDbContextType = new MultiTenantDbContextType(originalDbContextType, entry.Key.MultiTenancySide);
opts.DbContextReplacements[multiTenantDbContextType] = targetDbContextType;
});
}

17
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs

@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.EntityFrameworkCore;
@ -15,14 +18,14 @@ public class AbpDbContextOptions
internal Dictionary<Type, object> ConfigureActions { get; }
internal Dictionary<Type, Type> DbContextReplacements { get; }
internal Dictionary<MultiTenantDbContextType, Type> DbContextReplacements { get; }
public AbpDbContextOptions()
{
DefaultPreConfigureActions = new List<Action<AbpDbContextConfigurationContext>>();
PreConfigureActions = new Dictionary<Type, List<object>>();
ConfigureActions = new Dictionary<Type, object>();
DbContextReplacements = new Dictionary<Type, Type>();
DbContextReplacements = new Dictionary<MultiTenantDbContextType, Type>();
}
public void PreConfigure([NotNull] Action<AbpDbContextConfigurationContext> action)
@ -76,22 +79,22 @@ public class AbpDbContextOptions
return ConfigureActions.ContainsKey(dbContextType);
}
internal Type GetReplacedTypeOrSelf(Type dbContextType)
internal Type GetReplacedTypeOrSelf(Type dbContextType, MultiTenancySides multiTenancySides = MultiTenancySides.Both)
{
var replacementType = dbContextType;
while (true)
{
if (DbContextReplacements.TryGetValue(replacementType, out var foundType))
var foundType = DbContextReplacements.FirstOrDefault(x => x.Key.Type == replacementType && x.Key.MultiTenancySide.HasFlag(multiTenancySides));
if (!foundType.Equals(default(KeyValuePair<MultiTenantDbContextType, Type>)))
{
if (foundType == dbContextType)
if (foundType.Value == dbContextType)
{
throw new AbpException(
"Circular DbContext replacement found for " +
dbContextType.AssemblyQualifiedName
);
}
replacementType = foundType;
replacementType = foundType.Value;
}
else
{

15
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreDbContextTypeProvider.cs

@ -1,26 +1,23 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.EntityFrameworkCore;
public class EfCoreDbContextTypeProvider : IEfCoreDbContextTypeProvider, ITransientDependency
{
private readonly AbpDbContextOptions _options;
private readonly ICurrentTenant _currentTenant;
public EfCoreDbContextTypeProvider(IOptions<AbpDbContextOptions> options)
public EfCoreDbContextTypeProvider(IOptions<AbpDbContextOptions> options, ICurrentTenant currentTenant)
{
_currentTenant = currentTenant;
_options = options.Value;
}
public Type GetDbContextType(Type dbContextType)
public virtual Type GetDbContextType(Type dbContextType)
{
return _options.GetReplacedTypeOrSelf(dbContextType);
}
public virtual Task<Type> GetDbContextTypeAsync(Type dbContextType)
{
return Task.FromResult(_options.GetReplacedTypeOrSelf(dbContextType));
return _options.GetReplacedTypeOrSelf(dbContextType, _currentTenant.GetMultiTenancySide());
}
}

3
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IEfCoreDbContextTypeProvider.cs

@ -1,11 +1,8 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.EntityFrameworkCore;
public interface IEfCoreDbContextTypeProvider
{
Type GetDbContextType(Type dbContextType);
Task<Type> GetDbContextTypeAsync(Type dbContextType);
}

69
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs

@ -22,12 +22,11 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
public ILogger<UnitOfWorkDbContextProvider<TDbContext>> Logger { get; set; }
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IConnectionStringResolver _connectionStringResolver;
private readonly ICancellationTokenProvider _cancellationTokenProvider;
private readonly ICurrentTenant _currentTenant;
private readonly AbpDbContextOptions _options;
private readonly IEfCoreDbContextTypeProvider _efCoreDbContextTypeProvider;
protected readonly IUnitOfWorkManager UnitOfWorkManager;
protected readonly IConnectionStringResolver ConnectionStringResolver;
protected readonly ICancellationTokenProvider CancellationTokenProvider;
protected readonly ICurrentTenant CurrentTenant;
protected readonly IEfCoreDbContextTypeProvider EfCoreDbContextTypeProvider;
public UnitOfWorkDbContextProvider(
IUnitOfWorkManager unitOfWorkManager,
@ -36,20 +35,20 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
ICurrentTenant currentTenant,
IEfCoreDbContextTypeProvider efCoreDbContextTypeProvider)
{
_unitOfWorkManager = unitOfWorkManager;
_connectionStringResolver = connectionStringResolver;
_cancellationTokenProvider = cancellationTokenProvider;
_currentTenant = currentTenant;
_efCoreDbContextTypeProvider = efCoreDbContextTypeProvider;
UnitOfWorkManager = unitOfWorkManager;
ConnectionStringResolver = connectionStringResolver;
CancellationTokenProvider = cancellationTokenProvider;
CurrentTenant = currentTenant;
EfCoreDbContextTypeProvider = efCoreDbContextTypeProvider;
Logger = NullLogger<UnitOfWorkDbContextProvider<TDbContext>>.Instance;
}
[Obsolete("Use GetDbContextAsync method.")]
public TDbContext GetDbContext()
public virtual TDbContext GetDbContext()
{
if (UnitOfWork.EnableObsoleteDbContextCreationWarning &&
!UnitOfWorkManager.DisableObsoleteDbContextCreationWarning.Value)
!Uow.UnitOfWorkManager.DisableObsoleteDbContextCreationWarning.Value)
{
Logger.LogWarning(
"UnitOfWorkDbContextProvider.GetDbContext is deprecated. Use GetDbContextAsync instead! " +
@ -59,13 +58,13 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
Logger.LogWarning(Environment.StackTrace.Truncate(2048));
}
var unitOfWork = _unitOfWorkManager.Current;
var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException("A DbContext can only be created inside a unit of work!");
}
var targetDbContextType = _efCoreDbContextTypeProvider.GetDbContextType(typeof(TDbContext));
var targetDbContextType = EfCoreDbContextTypeProvider.GetDbContextType(typeof(TDbContext));
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName(targetDbContextType);
var connectionString = ResolveConnectionString(connectionStringName);
var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}";
@ -79,15 +78,15 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
return (TDbContext)((EfCoreDatabaseApi)databaseApi).DbContext;
}
public async Task<TDbContext> GetDbContextAsync()
public virtual async Task<TDbContext> GetDbContextAsync()
{
var unitOfWork = _unitOfWorkManager.Current;
var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException("A DbContext can only be created inside a unit of work!");
}
var targetDbContextType = await _efCoreDbContextTypeProvider.GetDbContextTypeAsync(typeof(TDbContext));
var targetDbContextType = EfCoreDbContextTypeProvider.GetDbContextType(typeof(TDbContext));
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName(targetDbContextType);
var connectionString = await ResolveConnectionStringAsync(connectionStringName);
@ -108,7 +107,7 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
}
[Obsolete("Use CreateDbContextAsync method.")]
private TDbContext CreateDbContext(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)
protected virtual TDbContext CreateDbContext(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)
{
var creationContext = new DbContextCreationContext(connectionStringName, connectionString);
using (DbContextCreationContext.Use(creationContext))
@ -128,7 +127,7 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
}
}
private async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)
protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)
{
var creationContext = new DbContextCreationContext(connectionStringName, connectionString);
using (DbContextCreationContext.Use(creationContext))
@ -149,14 +148,14 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
}
[Obsolete("Use CreateDbContextAsync.")]
private TDbContext CreateDbContext(IUnitOfWork unitOfWork)
protected virtual TDbContext CreateDbContext(IUnitOfWork unitOfWork)
{
return unitOfWork.Options.IsTransactional
? CreateDbContextWithTransaction(unitOfWork)
: unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
}
private async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork)
protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork)
{
return unitOfWork.Options.IsTransactional
? await CreateDbContextWithTransactionAsync(unitOfWork)
@ -164,7 +163,7 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
}
[Obsolete("Use CreateDbContextWithTransactionAsync.")]
private TDbContext CreateDbContextWithTransaction(IUnitOfWork unitOfWork)
protected virtual TDbContext CreateDbContextWithTransaction(IUnitOfWork unitOfWork)
{
var transactionApiKey = $"EntityFrameworkCore_{DbContextCreationContext.Current.ConnectionString}";
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as EfCoreTransactionApi;
@ -184,7 +183,7 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
new EfCoreTransactionApi(
dbtransaction,
dbContext,
_cancellationTokenProvider
CancellationTokenProvider
)
);
}
@ -259,7 +258,7 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
}
}
private async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
protected virtual async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
{
var transactionApiKey = $"EntityFrameworkCore_{DbContextCreationContext.Current.ConnectionString}";
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as EfCoreTransactionApi;
@ -279,7 +278,7 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
new EfCoreTransactionApi(
dbTransaction,
dbContext,
_cancellationTokenProvider
CancellationTokenProvider
)
);
}
@ -359,37 +358,37 @@ public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbCon
}
}
private async Task<string> ResolveConnectionStringAsync(string connectionStringName)
protected virtual async Task<string> ResolveConnectionStringAsync(string connectionStringName)
{
// Multi-tenancy unaware contexts should always use the host connection string
if (typeof(TDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
{
using (_currentTenant.Change(null))
using (CurrentTenant.Change(null))
{
return await _connectionStringResolver.ResolveAsync(connectionStringName);
return await ConnectionStringResolver.ResolveAsync(connectionStringName);
}
}
return await _connectionStringResolver.ResolveAsync(connectionStringName);
return await ConnectionStringResolver.ResolveAsync(connectionStringName);
}
[Obsolete("Use ResolveConnectionStringAsync method.")]
private string ResolveConnectionString(string connectionStringName)
protected virtual string ResolveConnectionString(string connectionStringName)
{
// Multi-tenancy unaware contexts should always use the host connection string
if (typeof(TDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
{
using (_currentTenant.Change(null))
using (CurrentTenant.Change(null))
{
return _connectionStringResolver.Resolve(connectionStringName);
return ConnectionStringResolver.Resolve(connectionStringName);
}
}
return _connectionStringResolver.Resolve(connectionStringName);
return ConnectionStringResolver.Resolve(connectionStringName);
}
protected virtual CancellationToken GetCancellationToken(CancellationToken preferredValue = default)
{
return _cancellationTokenProvider.FallbackToProvider(preferredValue);
return CancellationTokenProvider.FallbackToProvider(preferredValue);
}
}

2
framework/src/Volo.Abp.MemoryDb/Microsoft/Extensions/DependencyInjection/AbpMemoryDbServiceCollectionExtensions.cs

@ -20,7 +20,7 @@ public static class AbpMemoryDbServiceCollectionExtensions
foreach (var entry in options.ReplacedDbContextTypes)
{
var originalDbContextType = entry.Key;
var originalDbContextType = entry.Key.Type;
var targetDbContextType = entry.Value ?? typeof(TMemoryDbContext);
services.Replace(

18
framework/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs

@ -20,26 +20,26 @@ public static class AbpMongoDbServiceCollectionExtensions
foreach (var dbContextType in replacedDbContextTypes)
{
options.ReplaceDbContext(dbContextType);
options.ReplaceDbContext(dbContextType.Type, multiTenancySides: dbContextType.MultiTenancySide);
}
optionsBuilder?.Invoke(options);
foreach (var entry in options.ReplacedDbContextTypes)
{
var originalDbContextType = entry.Key;
var originalDbContextType = entry.Key.Type;
var targetDbContextType = entry.Value ?? typeof(TMongoDbContext);
services.Replace(
ServiceDescriptor.Transient(
originalDbContextType,
sp => sp.GetRequiredService(targetDbContextType)
)
);
services.Replace(ServiceDescriptor.Transient(originalDbContextType, sp =>
{
var dbContextType = sp.GetRequiredService<IMongoDbContextTypeProvider>().GetDbContextType(originalDbContextType);
return sp.GetRequiredService(dbContextType);
}));
services.Configure<AbpMongoDbContextOptions>(opts =>
{
opts.DbContextReplacements[originalDbContextType] = targetDbContextType;
var multiTenantDbContextType = new MultiTenantDbContextType(originalDbContextType, entry.Key.MultiTenancySide);
opts.DbContextReplacements[multiTenantDbContextType] = targetDbContextType;
});
}

17
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContextOptions.cs

@ -1,37 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.MongoDB;
public class AbpMongoDbContextOptions
{
internal Dictionary<Type, Type> DbContextReplacements { get; }
internal Dictionary<MultiTenantDbContextType, Type> DbContextReplacements { get; }
public Action<MongoClientSettings> MongoClientSettingsConfigurer { get; set; }
public AbpMongoDbContextOptions()
{
DbContextReplacements = new Dictionary<Type, Type>();
DbContextReplacements = new Dictionary<MultiTenantDbContextType, Type>();
}
internal Type GetReplacedTypeOrSelf(Type dbContextType)
internal Type GetReplacedTypeOrSelf(Type dbContextType, MultiTenancySides multiTenancySides = MultiTenancySides.Both)
{
var replacementType = dbContextType;
while (true)
{
if (DbContextReplacements.TryGetValue(replacementType, out var foundType))
var foundType = DbContextReplacements.FirstOrDefault(x => x.Key.Type == replacementType && x.Key.MultiTenancySide.HasFlag(multiTenancySides));
if (!foundType.Equals(default(KeyValuePair<MultiTenantDbContextType, Type>)))
{
if (foundType == dbContextType)
if (foundType.Value == dbContextType)
{
throw new AbpException(
"Circular DbContext replacement found for " +
dbContextType.AssemblyQualifiedName
);
}
replacementType = foundType;
replacementType = foundType.Value;
}
else
{

3
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoDbContextTypeProvider.cs

@ -1,11 +1,8 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.MongoDB;
public interface IMongoDbContextTypeProvider
{
Type GetDbContextType(Type dbContextType);
Task<Type> GetDbContextTypeAsync(Type dbContextType);
}

15
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbContextTypeProvider.cs

@ -1,26 +1,23 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.MongoDB;
public class MongoDbContextTypeProvider : IMongoDbContextTypeProvider, ITransientDependency
{
private readonly AbpMongoDbContextOptions _options;
private readonly ICurrentTenant _currentTenant;
public MongoDbContextTypeProvider(IOptions<AbpMongoDbContextOptions> options)
public MongoDbContextTypeProvider(IOptions<AbpMongoDbContextOptions> options, ICurrentTenant currentTenant)
{
_currentTenant = currentTenant;
_options = options.Value;
}
public Type GetDbContextType(Type dbContextType)
public virtual Type GetDbContextType(Type dbContextType)
{
return _options.GetReplacedTypeOrSelf(dbContextType);
}
public virtual Task<Type> GetDbContextTypeAsync(Type dbContextType)
{
return Task.FromResult(_options.GetReplacedTypeOrSelf(dbContextType));
return _options.GetReplacedTypeOrSelf(dbContextType, _currentTenant.GetMultiTenancySide());
}
}

70
framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs

@ -20,12 +20,12 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
private const string TransactionsNotSupportedErrorMessage = "Current database does not support transactions. Your database may remain in an inconsistent state in an error case.";
public ILogger<UnitOfWorkMongoDbContextProvider<TMongoDbContext>> Logger { get; set; }
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IConnectionStringResolver _connectionStringResolver;
private readonly ICancellationTokenProvider _cancellationTokenProvider;
private readonly ICurrentTenant _currentTenant;
private readonly AbpMongoDbContextOptions _options;
private readonly IMongoDbContextTypeProvider _dbContextTypeProvider;
protected readonly IUnitOfWorkManager UnitOfWorkManager;
protected readonly IConnectionStringResolver ConnectionStringResolver;
protected readonly ICancellationTokenProvider CancellationTokenProvider;
protected readonly ICurrentTenant CurrentTenant;
protected readonly AbpMongoDbContextOptions Options;
protected readonly IMongoDbContextTypeProvider DbContextTypeProvider;
public UnitOfWorkMongoDbContextProvider(
IUnitOfWorkManager unitOfWorkManager,
@ -35,21 +35,21 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
IOptions<AbpMongoDbContextOptions> options,
IMongoDbContextTypeProvider dbContextTypeProvider)
{
_unitOfWorkManager = unitOfWorkManager;
_connectionStringResolver = connectionStringResolver;
_cancellationTokenProvider = cancellationTokenProvider;
_currentTenant = currentTenant;
_dbContextTypeProvider = dbContextTypeProvider;
_options = options.Value;
UnitOfWorkManager = unitOfWorkManager;
ConnectionStringResolver = connectionStringResolver;
CancellationTokenProvider = cancellationTokenProvider;
CurrentTenant = currentTenant;
DbContextTypeProvider = dbContextTypeProvider;
Options = options.Value;
Logger = NullLogger<UnitOfWorkMongoDbContextProvider<TMongoDbContext>>.Instance;
}
[Obsolete("Use CreateDbContextAsync")]
public TMongoDbContext GetDbContext()
public virtual TMongoDbContext GetDbContext()
{
if (UnitOfWork.EnableObsoleteDbContextCreationWarning &&
!UnitOfWorkManager.DisableObsoleteDbContextCreationWarning.Value)
!Uow.UnitOfWorkManager.DisableObsoleteDbContextCreationWarning.Value)
{
Logger.LogWarning(
"UnitOfWorkDbContextProvider.GetDbContext is deprecated. Use GetDbContextAsync instead! " +
@ -59,14 +59,14 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
Logger.LogWarning(Environment.StackTrace.Truncate(2048));
}
var unitOfWork = _unitOfWorkManager.Current;
var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException(
$"A {nameof(IMongoDatabase)} instance can only be created inside a unit of work!");
}
var targetDbContextType = _dbContextTypeProvider.GetDbContextType(typeof(TMongoDbContext));
var targetDbContextType = DbContextTypeProvider.GetDbContextType(typeof(TMongoDbContext));
var connectionString = ResolveConnectionString(targetDbContextType);
var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}";
@ -85,16 +85,16 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
return (TMongoDbContext)((MongoDbDatabaseApi) databaseApi).DbContext;
}
public async Task<TMongoDbContext> GetDbContextAsync(CancellationToken cancellationToken = default)
public virtual async Task<TMongoDbContext> GetDbContextAsync(CancellationToken cancellationToken = default)
{
var unitOfWork = _unitOfWorkManager.Current;
var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException(
$"A {nameof(IMongoDatabase)} instance can only be created inside a unit of work!");
}
var targetDbContextType = await _dbContextTypeProvider.GetDbContextTypeAsync(typeof(TMongoDbContext));
var targetDbContextType = DbContextTypeProvider.GetDbContextType(typeof(TMongoDbContext));
var connectionString = await ResolveConnectionStringAsync(targetDbContextType);
var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}";
@ -142,7 +142,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
return dbContext;
}
private async Task<TMongoDbContext> CreateDbContextAsync(
protected virtual async Task<TMongoDbContext> CreateDbContextAsync(
IUnitOfWork unitOfWork,
MongoUrl mongoUrl,
string databaseName,
@ -169,7 +169,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
}
[Obsolete("Use CreateDbContextWithTransactionAsync")]
private TMongoDbContext CreateDbContextWithTransaction(
protected virtual TMongoDbContext CreateDbContextWithTransaction(
IUnitOfWork unitOfWork,
MongoUrl url,
MongoClient client,
@ -205,7 +205,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
transactionApiKey,
new MongoDbTransactionApi(
session,
_cancellationTokenProvider
CancellationTokenProvider
)
);
@ -219,7 +219,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
return dbContext;
}
private async Task<TMongoDbContext> CreateDbContextWithTransactionAsync(
protected virtual async Task<TMongoDbContext> CreateDbContextWithTransactionAsync(
IUnitOfWork unitOfWork,
MongoUrl url,
MongoClient client,
@ -256,7 +256,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
transactionApiKey,
new MongoDbTransactionApi(
session,
_cancellationTokenProvider
CancellationTokenProvider
)
);
@ -270,45 +270,45 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
return dbContext;
}
private async Task<string> ResolveConnectionStringAsync(Type dbContextType)
protected virtual async Task<string> ResolveConnectionStringAsync(Type dbContextType)
{
// Multi-tenancy unaware contexts should always use the host connection string
if (typeof(TMongoDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
{
using (_currentTenant.Change(null))
using (CurrentTenant.Change(null))
{
return await _connectionStringResolver.ResolveAsync(dbContextType);
return await ConnectionStringResolver.ResolveAsync(dbContextType);
}
}
return await _connectionStringResolver.ResolveAsync(dbContextType);
return await ConnectionStringResolver.ResolveAsync(dbContextType);
}
[Obsolete("Use ResolveConnectionStringAsync method.")]
private string ResolveConnectionString(Type dbContextType)
protected virtual string ResolveConnectionString(Type dbContextType)
{
// Multi-tenancy unaware contexts should always use the host connection string
if (typeof(TMongoDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
{
using (_currentTenant.Change(null))
using (CurrentTenant.Change(null))
{
return _connectionStringResolver.Resolve(dbContextType);
return ConnectionStringResolver.Resolve(dbContextType);
}
}
return _connectionStringResolver.Resolve(dbContextType);
return ConnectionStringResolver.Resolve(dbContextType);
}
private MongoClient CreateMongoClient(MongoUrl mongoUrl)
protected virtual MongoClient CreateMongoClient(MongoUrl mongoUrl)
{
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
_options.MongoClientSettingsConfigurer?.Invoke(mongoClientSettings);
Options.MongoClientSettingsConfigurer?.Invoke(mongoClientSettings);
return new MongoClient(mongoClientSettings);
}
protected virtual CancellationToken GetCancellationToken(CancellationToken preferredValue = default)
{
return _cancellationTokenProvider.FallbackToProvider(preferredValue);
return CancellationTokenProvider.FallbackToProvider(preferredValue);
}
}

15
framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContext.cs

@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
public class FifthDbContext : AbpDbContext<FifthDbContext>, IFifthDbContext
{
public DbSet<FifthDbContextDummyEntity> FifthDbContextDummyEntity { get; set; }
public DbSet<FifthDbContextMultiTenantDummyEntity> FifthDbContextMultiTenantDummyEntity { get; set; }
public FifthDbContext(DbContextOptions<FifthDbContext> options)
: base(options)
{
}
}

9
framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContextDummyEntity.cs

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
public class FifthDbContextDummyEntity : AggregateRoot<Guid>
{
public string Value { get; set; }
}

20
framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContextDummyEntityRepository.cs

@ -0,0 +1,20 @@
using System;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
public interface IFifthDbContextDummyEntityRepository : IBasicRepository<FifthDbContextDummyEntity, Guid>
{
}
public class FifthDbContextDummyEntityRepository :
EfCoreRepository<IFifthDbContext, FifthDbContextDummyEntity, Guid>,
IFifthDbContextDummyEntityRepository
{
public FifthDbContextDummyEntityRepository(IDbContextProvider<IFifthDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}

12
framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContextMultiTenantDummyEntity.cs

@ -0,0 +1,12 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
public class FifthDbContextMultiTenantDummyEntity : AggregateRoot<Guid>, IMultiTenant
{
public string Value { get; set; }
public Guid? TenantId { get; set; }
}

20
framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/FifthDbContextMultiTenantDummyEntityRepository.cs

@ -0,0 +1,20 @@
using System;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
public interface IFifthDbContextMultiTenantDummyEntityRepository : IBasicRepository<FifthDbContextMultiTenantDummyEntity, Guid>
{
}
public class FifthDbContextMultiTenantDummyEntityRepository :
EfCoreRepository<IFifthDbContext, FifthDbContextMultiTenantDummyEntity, Guid>,
IFifthDbContextMultiTenantDummyEntityRepository
{
public FifthDbContextMultiTenantDummyEntityRepository(IDbContextProvider<IFifthDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}

10
framework/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/FifthContext/IFifthDbContext.cs

@ -0,0 +1,10 @@
using Microsoft.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
public interface IFifthDbContext : IEfCoreDbContext
{
DbSet<FifthDbContextDummyEntity> FifthDbContextDummyEntity { get; set; }
DbSet<FifthDbContextMultiTenantDummyEntity> FifthDbContextMultiTenantDummyEntity { get; set; }
}

13
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs

@ -6,9 +6,11 @@ using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore.Domain;
using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TestApp;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.TestApp.EntityFrameworkCore;
@ -44,6 +46,17 @@ public class AbpEntityFrameworkCoreTestModule : AbpModule
});
});
context.Services.AddAbpDbContext<HostTestAppDbContext>(options =>
{
options.AddDefaultRepositories(true);
options.ReplaceDbContext<IFifthDbContext>(MultiTenancySides.Host);
});
context.Services.AddAbpDbContext<TenantTestAppDbContext>(options =>
{
options.AddDefaultRepositories(true);
});
var sqliteConnection = CreateDatabaseAndGetConnection();
Configure<AbpDbContextOptions>(options =>

49
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DbContext_Replace_Tests.cs

@ -3,8 +3,10 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
using Volo.Abp.EntityFrameworkCore.TestApp.FourthContext;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.TestApp.EntityFrameworkCore;
using Volo.Abp.Uow;
@ -32,8 +34,8 @@ public class DbContext_Replace_Tests : EntityFrameworkCoreTestBase
[Fact]
public async Task Should_Replace_DbContext()
{
(await _dbContextTypeProvider.GetDbContextTypeAsync(typeof(IThirdDbContext))).ShouldBe(typeof(TestAppDbContext));
(await _dbContextTypeProvider.GetDbContextTypeAsync(typeof(IFourthDbContext))).ShouldBe(typeof(TestAppDbContext));
_dbContextTypeProvider.GetDbContextType(typeof(IThirdDbContext)).ShouldBe(typeof(TestAppDbContext));
_dbContextTypeProvider.GetDbContextType(typeof(IFourthDbContext)).ShouldBe(typeof(TestAppDbContext));
(ServiceProvider.GetRequiredService<IThirdDbContext>() is TestAppDbContext).ShouldBeTrue();
(ServiceProvider.GetRequiredService<IFourthDbContext>() is TestAppDbContext).ShouldBeTrue();
@ -64,4 +66,47 @@ public class DbContext_Replace_Tests : EntityFrameworkCoreTestBase
await uow.CompleteAsync();
}
}
[Fact]
public async Task Should_Replace_DbContext_By_Host_And_Tenant()
{
_dbContextTypeProvider.GetDbContextType(typeof(IFifthDbContext)).ShouldBe(typeof(HostTestAppDbContext));
(ServiceProvider.GetRequiredService<IFifthDbContext>() is HostTestAppDbContext).ShouldBeTrue();
using (var uow = _unitOfWorkManager.Begin())
{
var instance1 = await GetRequiredService<IBasicRepository<FifthDbContextDummyEntity, Guid>>().GetDbContextAsync();
(instance1 is IFifthDbContext).ShouldBeTrue();
var instance2 = await GetRequiredService<IBasicRepository<FifthDbContextMultiTenantDummyEntity, Guid>>().GetDbContextAsync();
(instance2 is HostTestAppDbContext).ShouldBeTrue();
instance1.ShouldBe(instance2);
await uow.CompleteAsync();
}
using (GetRequiredService<ICurrentTenant>().Change(Guid.NewGuid()))
{
_dbContextTypeProvider.GetDbContextType(typeof(IFifthDbContext)).ShouldBe(typeof(TenantTestAppDbContext));
(ServiceProvider.GetRequiredService<IFifthDbContext>() is TenantTestAppDbContext).ShouldBeTrue();
using (var uow = _unitOfWorkManager.Begin())
{
var instance1 = await GetRequiredService<IBasicRepository<FifthDbContextDummyEntity, Guid>>().GetDbContextAsync();
(instance1 is IFifthDbContext).ShouldBeTrue();
var instance2 = await GetRequiredService<IFifthDbContextDummyEntityRepository>().GetDbContextAsync();
(instance2 is HostTestAppDbContext).ShouldBeTrue();
instance1.ShouldBe(instance2);
// Multi-tenant entities use tenant DbContext
var instance3 = await GetRequiredService<IFifthDbContextMultiTenantDummyEntityRepository>().GetDbContextAsync();
(instance3 is TenantTestAppDbContext).ShouldBeTrue();
await uow.CompleteAsync();
}
}
}
}

16
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/HostTestAppDbContext.cs

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
namespace Volo.Abp.TestApp.EntityFrameworkCore;
public class HostTestAppDbContext : AbpDbContext<HostTestAppDbContext>, IFifthDbContext
{
public DbSet<FifthDbContextDummyEntity> FifthDbContextDummyEntity { get; set; }
public DbSet<FifthDbContextMultiTenantDummyEntity> FifthDbContextMultiTenantDummyEntity { get; set; }
public HostTestAppDbContext(DbContextOptions<HostTestAppDbContext> options)
: base(options)
{
}
}

19
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TenantTestAppDbContext.cs

@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.TestApp.FifthContext;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.TestApp.EntityFrameworkCore;
[ReplaceDbContext(typeof(IFifthDbContext), MultiTenancySides.Tenant)]
public class TenantTestAppDbContext : AbpDbContext<TenantTestAppDbContext>, IFifthDbContext
{
public DbSet<FifthDbContextDummyEntity> FifthDbContextDummyEntity { get; set; }
public DbSet<FifthDbContextMultiTenantDummyEntity> FifthDbContextMultiTenantDummyEntity { get; set; }
public TenantTestAppDbContext(DbContextOptions<TenantTestAppDbContext> options)
: base(options)
{
}
}

10
framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContext.cs

@ -0,0 +1,10 @@
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.TestApp.FifthContext;
public class FifthDbContext : AbpMongoDbContext, IFifthDbContext
{
public IMongoCollection<FifthDbContextDummyEntity> FifthDbContextDummyEntity { get; set; }
public IMongoCollection<FifthDbContextMultiTenantDummyEntity> FifthDbContextMultiTenantDummyEntity { get; set; }
}

9
framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContextDummyEntity.cs

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.MongoDB.TestApp.FifthContext;
public class FifthDbContextDummyEntity : AggregateRoot<Guid>
{
public string Value { get; set; }
}

26
framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContextDummyEntityRepository.cs

@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Repositories.MongoDB;
namespace Volo.Abp.MongoDB.TestApp.FifthContext;
public interface IFifthDbContextDummyEntityRepository : IBasicRepository<FifthDbContextDummyEntity, Guid>
{
Task<IAbpMongoDbContext> GetDbContextAsync();
}
public class FifthDbContextDummyEntityRepository :
MongoDbRepository<IFifthDbContext, FifthDbContextDummyEntity, Guid>,
IFifthDbContextDummyEntityRepository
{
public FifthDbContextDummyEntityRepository(IMongoDbContextProvider<IFifthDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task<IAbpMongoDbContext> GetDbContextAsync()
{
return await base.GetDbContextAsync();
}
}

12
framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContextMultiTenantDummyEntity.cs

@ -0,0 +1,12 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.MongoDB.TestApp.FifthContext;
public class FifthDbContextMultiTenantDummyEntity : AggregateRoot<Guid>, IMultiTenant
{
public string Value { get; set; }
public Guid? TenantId { get; set; }
}

26
framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/FifthDbContextMultiTenantDummyEntityRepository.cs

@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Repositories.MongoDB;
namespace Volo.Abp.MongoDB.TestApp.FifthContext;
public interface IFifthDbContextMultiTenantDummyEntityRepository : IBasicRepository<FifthDbContextMultiTenantDummyEntity, Guid>
{
Task<IAbpMongoDbContext> GetDbContextAsync();
}
public class FifthDbContextMultiTenantDummyEntityRepository :
MongoDbRepository<IFifthDbContext, FifthDbContextMultiTenantDummyEntity, Guid>,
IFifthDbContextMultiTenantDummyEntityRepository
{
public FifthDbContextMultiTenantDummyEntityRepository(IMongoDbContextProvider<IFifthDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task<IAbpMongoDbContext> GetDbContextAsync()
{
return await base.GetDbContextAsync();
}
}

10
framework/test/Volo.Abp.MongoDB.Tests.SecondContext/Volo/Abp/MongoDB/TestApp/FifthContext/IFifthDbContext.cs

@ -0,0 +1,10 @@
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.TestApp.FifthContext;
public interface IFifthDbContext : IAbpMongoDbContext
{
IMongoCollection<FifthDbContextDummyEntity> FifthDbContextDummyEntity { get; }
IMongoCollection<FifthDbContextMultiTenantDummyEntity> FifthDbContextMultiTenantDummyEntity { get; }
}

14
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs

@ -2,10 +2,13 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB.TestApp.FifthContext;
using Volo.Abp.MongoDB.TestApp.SecondContext;
using Volo.Abp.MongoDB.TestApp.ThirdDbContext;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TestApp;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.TestApp.MongoDb;
using Volo.Abp.TestApp.MongoDB;
namespace Volo.Abp.MongoDB;
@ -35,5 +38,16 @@ public class AbpMongoDbTestModule : AbpModule
options.ReplaceDbContext<IThirdDbContext>();
});
context.Services.AddMongoDbContext<HostTestAppDbContext>(options =>
{
options.AddDefaultRepositories<IFifthDbContext>();
options.ReplaceDbContext<IFifthDbContext>(MultiTenancySides.Host);
});
context.Services.AddMongoDbContext<TenantTestAppDbContext>(options =>
{
options.AddDefaultRepositories<IFifthDbContext>();
});
}
}

52
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DbContext_Replace_Tests.cs

@ -1,9 +1,15 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.MongoDB.TestApp.FifthContext;
using Volo.Abp.MongoDB.TestApp.FourthContext;
using Volo.Abp.MongoDB.TestApp.ThirdDbContext;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TestApp.MongoDb;
using Volo.Abp.TestApp.MongoDB;
using Volo.Abp.Uow;
using Xunit;
namespace Volo.Abp.MongoDB;
@ -19,12 +25,50 @@ public class DbContext_Replace_Tests : MongoDbTestBase
}
[Fact]
public async Task Should_Replace_DbContext()
public void Should_Replace_DbContext()
{
(await _dbContextTypeProvider.GetDbContextTypeAsync(typeof(IThirdDbContext))).ShouldBe(typeof(TestAppMongoDbContext));
(await _dbContextTypeProvider.GetDbContextTypeAsync(typeof(IFourthDbContext))).ShouldBe(typeof(TestAppMongoDbContext));
_dbContextTypeProvider.GetDbContextType(typeof(IThirdDbContext)).ShouldBe(typeof(TestAppMongoDbContext));
_dbContextTypeProvider.GetDbContextType(typeof(IFourthDbContext)).ShouldBe(typeof(TestAppMongoDbContext));
(ServiceProvider.GetRequiredService<IThirdDbContext>() is TestAppMongoDbContext).ShouldBeTrue();
(ServiceProvider.GetRequiredService<IFourthDbContext>() is TestAppMongoDbContext).ShouldBeTrue();
}
[Fact]
public async Task Should_Replace_DbContext_By_Host_And_Tenant()
{
_dbContextTypeProvider.GetDbContextType(typeof(IFifthDbContext)).ShouldBe(typeof(HostTestAppDbContext));
(ServiceProvider.GetRequiredService<IFifthDbContext>() is HostTestAppDbContext).ShouldBeTrue();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var instance1 = await GetRequiredService<IFifthDbContextDummyEntityRepository>().GetDbContextAsync();
(instance1 is HostTestAppDbContext).ShouldBeTrue();
var instance2 = await GetRequiredService<IFifthDbContextMultiTenantDummyEntityRepository>().GetDbContextAsync();
(instance2 is HostTestAppDbContext).ShouldBeTrue();
instance1.ShouldBe(instance2);
await uow.CompleteAsync();
}
using (GetRequiredService<ICurrentTenant>().Change(Guid.NewGuid()))
{
_dbContextTypeProvider.GetDbContextType(typeof(IFifthDbContext)).ShouldBe(typeof(TenantTestAppDbContext));
(ServiceProvider.GetRequiredService<IFifthDbContext>() is TenantTestAppDbContext).ShouldBeTrue();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var instance1 = await GetRequiredService<IFifthDbContextDummyEntityRepository>().GetDbContextAsync();
(instance1 is HostTestAppDbContext).ShouldBeTrue();
// Multi-tenant entities use tenant DbContext
var instance2 = await GetRequiredService<IFifthDbContextMultiTenantDummyEntityRepository>().GetDbContextAsync();
(instance2 is TenantTestAppDbContext).ShouldBeTrue();
await uow.CompleteAsync();
}
}
}
}

12
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/HostTestAppDbContext.cs

@ -0,0 +1,12 @@
using MongoDB.Driver;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.TestApp.FifthContext;
namespace Volo.Abp.TestApp.MongoDb;
public class HostTestAppDbContext : AbpMongoDbContext, IFifthDbContext
{
public IMongoCollection<FifthDbContextDummyEntity> FifthDbContextDummyEntity => Collection<FifthDbContextDummyEntity>();
public IMongoCollection<FifthDbContextMultiTenantDummyEntity> FifthDbContextMultiTenantDummyEntity => Collection<FifthDbContextMultiTenantDummyEntity>();
}

15
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TenantTestAppDbContext.cs

@ -0,0 +1,15 @@
using MongoDB.Driver;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.TestApp.FifthContext;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.TestApp.MongoDb;
[ReplaceDbContext(typeof(IFifthDbContext), MultiTenancySides.Tenant)]
public class TenantTestAppDbContext : AbpMongoDbContext, IFifthDbContext
{
public IMongoCollection<FifthDbContextDummyEntity> FifthDbContextDummyEntity => Collection<FifthDbContextDummyEntity>();
public IMongoCollection<FifthDbContextMultiTenantDummyEntity> FifthDbContextMultiTenantDummyEntity => Collection<FifthDbContextMultiTenantDummyEntity>();
}
Loading…
Cancel
Save