Browse Source

Implemented #7737 for EF Core: Should return the same instance in the same UOW for replaced DbContexts.

pull/7742/head
Halil İbrahim Kalkan 5 years ago
parent
commit
82d370b385
  1. 12
      framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs
  2. 2
      framework/src/Volo.Abp.EntityFrameworkCore/Properties/AssemblyInfo.cs
  3. 24
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs
  4. 8
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreDatabaseApi.cs
  5. 22
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs
  6. 26
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DbContext_Replace_Tests.cs

12
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<AbpDbContextOptions>(opts =>
{
opts.DbContextReplacements[dbContextType] = typeof(TDbContext);
});
}
new EfCoreRepositoryRegistrar(options).AddRepositories();

2
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

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

@ -7,19 +7,22 @@ namespace Volo.Abp.EntityFrameworkCore
{
public class AbpDbContextOptions
{
internal List<Action<AbpDbContextConfigurationContext>> DefaultPreConfigureActions { get; set; }
internal List<Action<AbpDbContextConfigurationContext>> DefaultPreConfigureActions { get; }
internal Action<AbpDbContextConfigurationContext> DefaultConfigureAction { get; set; }
internal Dictionary<Type, List<object>> PreConfigureActions { get; set; }
internal Dictionary<Type, List<object>> PreConfigureActions { get; }
internal Dictionary<Type, object> ConfigureActions { get; set; }
internal Dictionary<Type, object> ConfigureActions { get; }
internal Dictionary<Type, 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>();
}
public void PreConfigure([NotNull] Action<AbpDbContextConfigurationContext> action)
@ -57,5 +60,20 @@ namespace Volo.Abp.EntityFrameworkCore
ConfigureActions[typeof(TDbContext)] = action;
}
internal Type GetReplacedTypeOrSelf(Type dbContextType)
{
while (true)
{
if (DbContextReplacements.TryGetValue(dbContextType, out var foundType))
{
dbContextType = foundType;
}
else
{
return dbContextType;
}
}
}
}
}

8
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreDatabaseApi.cs

@ -1,15 +1,15 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.Uow.EntityFrameworkCore
{
public class EfCoreDatabaseApi<TDbContext> : 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;
}

22
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<TDbContext> : IDbContextProvider<TDbContext>
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<AbpDbContextOptions> options)
{
_unitOfWorkManager = unitOfWorkManager;
_connectionStringResolver = connectionStringResolver;
_cancellationTokenProvider = cancellationTokenProvider;
_currentTenant = currentTenant;
_options = options.Value;
Logger = NullLogger<UnitOfWorkDbContextProvider<TDbContext>>.Instance;
}
@ -63,15 +65,16 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>();
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<TDbContext>(
() => new EfCoreDatabaseApi(
CreateDbContext(unitOfWork, connectionStringName, connectionString)
));
return ((EfCoreDatabaseApi<TDbContext>)databaseApi).DbContext;
return (TDbContext)((EfCoreDatabaseApi)databaseApi).DbContext;
}
public async Task<TDbContext> GetDbContextAsync()
@ -85,20 +88,21 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>();
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<TDbContext>(
databaseApi = new EfCoreDatabaseApi(
await CreateDbContextAsync(unitOfWork, connectionStringName, connectionString)
);
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
}
return ((EfCoreDatabaseApi<TDbContext>)databaseApi).DbContext;
return (TDbContext)((EfCoreDatabaseApi)databaseApi).DbContext;
}
private TDbContext CreateDbContext(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)

26
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<ThirdDbContextDummyEntity, Guid> _dummyRepository;
private readonly IPersonRepository _personRepository;
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly AbpDbContextOptions _options;
public DbContext_Replace_Tests()
{
_dummyRepository = ServiceProvider.GetRequiredService<IBasicRepository<ThirdDbContextDummyEntity, Guid>>();
_unitOfWorkManager = ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
_dummyRepository = GetRequiredService<IBasicRepository<ThirdDbContextDummyEntity, Guid>>();
_personRepository = GetRequiredService<IPersonRepository>();
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
_options = GetRequiredService<IOptions<AbpDbContextOptions>>().Value;
}
[Fact]
public async Task Should_Replace_DbContext()
{
_options.GetReplacedTypeOrSelf(typeof(IThirdDbContext)).ShouldBe(typeof(TestAppDbContext));
(ServiceProvider.GetRequiredService<IThirdDbContext>() 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();
}
}

Loading…
Cancel
Save