Introduce ReplaceDbContextAttribute

pull/8897/head
liangshiwei 5 years ago
parent c4e92a3431
commit f39543add6

@ -395,6 +395,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Raz
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Scriban.Tests", "test\Volo.Abp.TextTemplating.Scriban.Tests\Volo.Abp.TextTemplating.Scriban.Tests.csproj", "{75D8DADB-3FA9-4C1D-B23A-DBFD08133B7C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MongoDB.Tests.SecondContext", "test\Volo.Abp.MongoDB.Tests.SecondContext\Volo.Abp.MongoDB.Tests.SecondContext.csproj", "{90B1866A-EF99-40B9-970E-B898E5AA523F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1177,6 +1179,10 @@ Global
{75D8DADB-3FA9-4C1D-B23A-DBFD08133B7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75D8DADB-3FA9-4C1D-B23A-DBFD08133B7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75D8DADB-3FA9-4C1D-B23A-DBFD08133B7C}.Release|Any CPU.Build.0 = Release|Any CPU
{90B1866A-EF99-40B9-970E-B898E5AA523F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90B1866A-EF99-40B9-970E-B898E5AA523F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90B1866A-EF99-40B9-970E-B898E5AA523F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90B1866A-EF99-40B9-970E-B898E5AA523F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1376,6 +1382,7 @@ Global
{42EA6F06-2D78-4D18-8AC4-8F2AB7E6DA19} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{C996F458-98FB-483D-9306-4701290E2FC1} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{75D8DADB-3FA9-4C1D-B23A-DBFD08133B7C} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{90B1866A-EF99-40B9-970E-B898E5AA523F} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

@ -0,0 +1,14 @@
using System;
namespace Volo.Abp.DependencyInjection
{
public class ReplaceDbContextAttribute : Attribute
{
public Type[] ReplacedDbContextTypes { get; }
public ReplaceDbContextAttribute(params Type[] replacedDbContextTypes)
{
ReplacedDbContextTypes = replacedDbContextTypes;
}
}
}

@ -1,6 +1,8 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Domain;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Uow.EntityFrameworkCore;
@ -9,6 +11,11 @@ namespace Volo.Abp.EntityFrameworkCore
[DependsOn(typeof(AbpDddDomainModule))]
public class AbpEntityFrameworkCoreModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddConventionalRegistrar(new AbpDbContextConventionalRegistrar());
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDbContextOptions>(options =>

@ -0,0 +1,37 @@
using System;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.EntityFrameworkCore.DependencyInjection
{
public class AbpDbContextConventionalRegistrar : DefaultConventionalRegistrar
{
public override void AddType(IServiceCollection services, Type type)
{
if (!typeof(IAbpEfCoreDbContext).IsAssignableFrom(type) || type == typeof(AbpDbContext<>))
{
return;
}
var replaceDbContextAttribute = type.GetCustomAttribute<ReplaceDbContextAttribute>(true);
if (replaceDbContextAttribute == null)
{
return;
}
foreach (var dbContextType in replaceDbContextAttribute.ReplacedDbContextTypes)
{
services.Replace(
ServiceDescriptor.Transient(
dbContextType,
sp => sp.GetRequiredService(type)
)
);
services.Configure<AbpDbContextOptions>(opts => { opts.DbContextReplacements[dbContextType] = type; });
}
}
}
}

@ -1,5 +1,7 @@
using System;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MongoDB.DependencyInjection
@ -22,6 +24,27 @@ namespace Volo.Abp.MongoDB.DependencyInjection
}
services.Add(ServiceDescriptor.Describe(typeof(IAbpMongoDbContext), type, ServiceLifetime.Transient));
var replaceDbContextAttribute = type.GetCustomAttribute<ReplaceDbContextAttribute>(true);
if (replaceDbContextAttribute == null)
{
return;
}
foreach (var dbContextType in replaceDbContextAttribute.ReplacedDbContextTypes)
{
services.Replace(
ServiceDescriptor.Transient(
dbContextType,
sp => sp.GetRequiredService(type)
)
);
services.Configure<AbpMongoDbContextOptions>(opts =>
{
opts.DbContextReplacements[dbContextType] = type;
});
}
}
}
}

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
namespace Volo.Abp.EntityFrameworkCore.TestApp.FourthContext
{
/* This dbcontext is just for testing to replace dbcontext from the application using ReplaceDbContextAttribute
*/
public class FourthDbContext : AbpDbContext<FourthDbContext>, IFourthDbContext
{
public DbSet<FourthDbContextDummyEntity> FourthDummyEntities { get; set; }
public FourthDbContext(DbContextOptions<FourthDbContext> options)
: base(options)
{
}
}
}

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

@ -0,0 +1,9 @@
using Microsoft.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.TestApp.FourthContext
{
public interface IFourthDbContext : IEfCoreDbContext
{
DbSet<FourthDbContextDummyEntity> FourthDummyEntities { get; set; }
}
}

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.TestApp.FourthContext;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
@ -19,6 +20,11 @@ namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext
{
options.AddDefaultRepositories<IThirdDbContext>();
});
context.Services.AddAbpDbContext<FourthDbContext>(options =>
{
options.AddDefaultRepositories<IFourthDbContext>();
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -36,4 +42,4 @@ namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext
}
}
}
}
}

@ -39,7 +39,7 @@ namespace Volo.Abp.EntityFrameworkCore
{
opt.DefaultWithDetailsFunc = q => q.Include(p => p.Phones);
});
options.Entity<Author>(opt =>
{
opt.DefaultWithDetailsFunc = q => q.Include(p => p.Books);
@ -76,7 +76,7 @@ namespace Volo.Abp.EntityFrameworkCore
@"CREATE VIEW View_PersonView AS
SELECT Name, CreationTime, Birthday, LastActive FROM People");
}
return connection;
}
}

@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EntityFrameworkCore.TestApp.FourthContext;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.TestApp.EntityFrameworkCore;
@ -15,13 +16,15 @@ namespace Volo.Abp.EntityFrameworkCore
public class DbContext_Replace_Tests : EntityFrameworkCoreTestBase
{
private readonly IBasicRepository<ThirdDbContextDummyEntity, Guid> _dummyRepository;
private readonly IPersonRepository _personRepository;
private readonly IBasicRepository<FourthDbContextDummyEntity, Guid> _fourthDummyRepository;
private readonly IPersonRepository _personRepository;
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly AbpDbContextOptions _options;
public DbContext_Replace_Tests()
{
_dummyRepository = GetRequiredService<IBasicRepository<ThirdDbContextDummyEntity, Guid>>();
_fourthDummyRepository = GetRequiredService<IBasicRepository<FourthDbContextDummyEntity, Guid>>();
_personRepository = GetRequiredService<IPersonRepository>();
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
_options = GetRequiredService<IOptions<AbpDbContextOptions>>().Value;
@ -31,8 +34,10 @@ namespace Volo.Abp.EntityFrameworkCore
public async Task Should_Replace_DbContext()
{
_options.GetReplacedTypeOrSelf(typeof(IThirdDbContext)).ShouldBe(typeof(TestAppDbContext));
_options.GetReplacedTypeOrSelf(typeof(IFourthDbContext)).ShouldBe(typeof(TestAppDbContext));
(ServiceProvider.GetRequiredService<IThirdDbContext>() is TestAppDbContext).ShouldBeTrue();
(ServiceProvider.GetRequiredService<IFourthDbContext>() is TestAppDbContext).ShouldBeTrue();
using (var uow = _unitOfWorkManager.Begin())
{
@ -44,11 +49,19 @@ namespace Volo.Abp.EntityFrameworkCore
var instance3 = await _personRepository.GetDbContextAsync();
(instance3 is TestAppDbContext).ShouldBeTrue();
var instance4 = await _fourthDummyRepository.GetDbContextAsync();
(instance4 is IFourthDbContext).ShouldBeTrue();
var instance5 = await _fourthDummyRepository.GetDbContextAsync();
(instance5 is TestAppDbContext).ShouldBeTrue();
// All instances should be the same!
instance3.ShouldBe(instance1);
instance3.ShouldBe(instance2);
instance3.ShouldBe(instance4);
instance3.ShouldBe(instance5);
await uow.CompleteAsync();
}
}

@ -1,12 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.TestApp.FourthContext;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.TestApp.Domain;
namespace Volo.Abp.TestApp.EntityFrameworkCore
{
public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext
[ReplaceDbContext(typeof(IFourthDbContext))]
public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext, IFourthDbContext
{
private DbSet<FourthDbContextDummyEntity> _dummyEntities;
private DbSet<FourthDbContextDummyEntity> _dummyEntities1;
public DbSet<Person> People { get; set; }
public DbSet<City> Cities { get; set; }
@ -16,10 +21,12 @@ namespace Volo.Abp.TestApp.EntityFrameworkCore
public DbSet<ThirdDbContextDummyEntity> DummyEntities { get; set; }
public DbSet<EntityWithIntPk> EntityWithIntPks { get; set; }
public DbSet<Author> Author { get; set; }
public TestAppDbContext(DbContextOptions<TestAppDbContext> options)
public DbSet<FourthDbContextDummyEntity> FourthDummyEntities { get; set; }
public TestAppDbContext(DbContextOptions<TestAppDbContext> options)
: base(options)
{

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<PreserveCompilationContext>true</PreserveCompilationContext>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.MongoDB\Volo.Abp.MongoDB.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,13 @@
using MongoDB.Driver;
using Volo.Abp.EntityFrameworkCore.TestApp.FourthContext;
namespace Volo.Abp.MongoDB.TestApp.FourthContext
{
/* This dbcontext is just for testing to replace dbcontext from the application using ReplaceDbContextAttribute
*/
public class FourthDbContext : AbpMongoDbContext, IFourthDbContext
{
public IMongoCollection<FourthDbContextDummyEntity> FourthDummyEntities => Collection<FourthDbContextDummyEntity>();
}
}

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

@ -0,0 +1,10 @@
using MongoDB.Driver;
using Volo.Abp.EntityFrameworkCore.TestApp.FourthContext;
namespace Volo.Abp.MongoDB.TestApp.FourthContext
{
public interface IFourthDbContext : IAbpMongoDbContext
{
IMongoCollection<FourthDbContextDummyEntity> FourthDummyEntities { get;}
}
}

@ -0,0 +1,45 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB.TestApp.FourthContext;
using Volo.Abp.MongoDB.TestApp.ThirdDbContext;
using Volo.Abp.Threading;
namespace Volo.Abp.MongoDB.TestApp.SecondContext
{
[DependsOn(typeof(AbpMongoDbModule))]
public class AbpMongoDbTestSecondContextModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddMongoDbContext<SecondDbContext>(options =>
{
options.AddDefaultRepositories();
});
context.Services.AddMongoDbContext<ThirdDbContext.ThirdDbContext>(options =>
{
options.AddDefaultRepositories<IThirdDbContext>();
});
context.Services.AddMongoDbContext<FourthDbContext>(options =>
{
options.AddDefaultRepositories<IFourthDbContext>();
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
SeedTestData(context);
}
private static void SeedTestData(ApplicationInitializationContext context)
{
using (var scope = context.ServiceProvider.CreateScope())
{
AsyncHelper.RunSync(() => scope.ServiceProvider
.GetRequiredService<SecondContextTestDataBuilder>()
.BuildAsync());
}
}
}
}

@ -0,0 +1,21 @@
using System;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.MongoDB.TestApp.SecondContext
{
public class BookInSecondDbContext : AggregateRoot<Guid>
{
public string Name { get; set; }
public BookInSecondDbContext()
{
}
public BookInSecondDbContext(Guid id, string name)
: base(id)
{
Name = name;
}
}
}

@ -0,0 +1,18 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.MongoDB.TestApp.SecondContext
{
public class PhoneInSecondDbContext : AggregateRoot
{
public virtual Guid PersonId { get; set; }
public virtual string Number { get; set; }
public override object[] GetKeys()
{
return new object[] {PersonId, Number};
}
}
}

@ -0,0 +1,30 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
namespace Volo.Abp.MongoDB.TestApp.SecondContext
{
public class SecondContextTestDataBuilder : ITransientDependency
{
private readonly IBasicRepository<BookInSecondDbContext, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
public SecondContextTestDataBuilder(IBasicRepository<BookInSecondDbContext, Guid> bookRepository, IGuidGenerator guidGenerator)
{
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
}
public async Task BuildAsync()
{
await _bookRepository.InsertAsync(
new BookInSecondDbContext(
_guidGenerator.Create(),
"TestBook1"
)
);
}
}
}

@ -0,0 +1,11 @@
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.TestApp.SecondContext
{
public class SecondDbContext : AbpMongoDbContext
{
public IMongoCollection<BookInSecondDbContext> Books => Collection<BookInSecondDbContext>();
public IMongoCollection<PhoneInSecondDbContext> Phones => Collection<PhoneInSecondDbContext>();
}
}

@ -0,0 +1,9 @@
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.TestApp.ThirdDbContext
{
public interface IThirdDbContext : IAbpMongoDbContext
{
IMongoCollection<ThirdDbContextDummyEntity> DummyEntities { get; }
}
}

@ -0,0 +1,11 @@
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.TestApp.ThirdDbContext
{
/* This dbcontext is just for testing to replace dbcontext from the application using AbpDbContextRegistrationOptions.ReplaceDbContext
*/
public class ThirdDbContext : AbpMongoDbContext, IThirdDbContext
{
public IMongoCollection<ThirdDbContextDummyEntity> DummyEntities => Collection<ThirdDbContextDummyEntity>();
}
}

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

@ -9,8 +9,8 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.MongoDB\Volo.Abp.MongoDB.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\Volo.Abp.MongoDB.Tests.SecondContext\Volo.Abp.MongoDB.Tests.SecondContext.csproj" />
<ProjectReference Include="..\Volo.Abp.TestApp\Volo.Abp.TestApp.csproj" />
</ItemGroup>

@ -1,21 +1,18 @@
using System;
using System.Linq;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Driver;
using MongoDB.Driver.Core.Servers;
using Volo.Abp.Data;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB.TestApp.SecondContext;
using Volo.Abp.MongoDB.TestApp.ThirdDbContext;
using Volo.Abp.TestApp;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.TestApp.MongoDB;
using Volo.Abp.Uow;
namespace Volo.Abp.MongoDB
{
[DependsOn(
typeof(AbpMongoDbModule),
typeof(TestAppModule)
typeof(TestAppModule),
typeof(AbpMongoDbTestSecondContextModule)
)]
public class AbpMongoDbTestModule : AbpModule
{
@ -35,6 +32,8 @@ namespace Volo.Abp.MongoDB
{
options.AddDefaultRepositories<ITestAppMongoDbContext>();
options.AddRepository<City, CityRepository>();
options.ReplaceDbContext<IThirdDbContext>();
});
}
}

@ -0,0 +1,31 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp.MongoDB.TestApp.FourthContext;
using Volo.Abp.MongoDB.TestApp.ThirdDbContext;
using Volo.Abp.TestApp.MongoDB;
using Xunit;
namespace Volo.Abp.MongoDB
{
[Collection(MongoTestCollection.Name)]
public class DbContext_Replace_Tests : MongoDbTestBase
{
private readonly AbpMongoDbContextOptions _options;
public DbContext_Replace_Tests()
{
_options = GetRequiredService<IOptions<AbpMongoDbContextOptions>>().Value;
}
[Fact]
public void Should_Replace_DbContext()
{
_options.GetReplacedTypeOrSelf(typeof(IThirdDbContext)).ShouldBe(typeof(TestAppMongoDbContext));
_options.GetReplacedTypeOrSelf(typeof(IFourthDbContext)).ShouldBe(typeof(TestAppMongoDbContext));
(ServiceProvider.GetRequiredService<IThirdDbContext>() is TestAppMongoDbContext).ShouldBeTrue();
(ServiceProvider.GetRequiredService<IFourthDbContext>() is TestAppMongoDbContext).ShouldBeTrue();
}
}
}

@ -1,12 +1,17 @@
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.TestApp.FourthContext;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.TestApp.FourthContext;
using Volo.Abp.MongoDB.TestApp.ThirdDbContext;
using Volo.Abp.TestApp.Domain;
namespace Volo.Abp.TestApp.MongoDB
{
[ConnectionStringName("TestApp")]
public class TestAppMongoDbContext : AbpMongoDbContext, ITestAppMongoDbContext
[ReplaceDbContext(typeof(IFourthDbContext))]
public class TestAppMongoDbContext : AbpMongoDbContext, ITestAppMongoDbContext, IThirdDbContext, IFourthDbContext
{
[MongoCollection("Persons")] //Intentionally changed the collection name to test it
public IMongoCollection<Person> People => Collection<Person>();
@ -15,6 +20,10 @@ namespace Volo.Abp.TestApp.MongoDB
public IMongoCollection<City> Cities => Collection<City>();
public IMongoCollection<ThirdDbContextDummyEntity> DummyEntities => Collection<ThirdDbContextDummyEntity>();
public IMongoCollection<FourthDbContextDummyEntity> FourthDummyEntities => Collection<FourthDbContextDummyEntity>();
protected internal override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);

Loading…
Cancel
Save