Skip to content

Commit

Permalink
Resolved #1051: Should get DbContext which does not define AutoReposi…
Browse files Browse the repository at this point in the history
…toryTypes attribute if more than one dbcontext
  • Loading branch information
hikalkan committed May 25, 2016
1 parent 143ba36 commit 8761b8c
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,24 +68,7 @@ private void RegisterGenericRepositoriesAndMatchDbContexes()

using (var dbContextMatcher = IocManager.ResolveAsDisposable<IDbContextTypeMatcher>())
{
foreach (var dbContextType in dbContextTypes)
{
var types = new List<Type>();
AddWithBaseTypes(dbContextType, types);
foreach (var type in types)
{
dbContextMatcher.Object.Add(type, dbContextType);
}
}
}
}

private static void AddWithBaseTypes(Type dbContextType, List<Type> types)
{
types.Add(dbContextType);
if (dbContextType != typeof(DbContext))
{
AddWithBaseTypes(dbContextType.BaseType, types);
dbContextMatcher.Object.Populate(dbContextTypes);
}
}
}
Expand Down
83 changes: 66 additions & 17 deletions src/Abp.EntityFramework/EntityFramework/DbContextTypeMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Abp.Collections.Extensions;
using Abp.Dependency;
using Abp.Domain.Uow;
using Abp.EntityFramework.Repositories;
using Abp.MultiTenancy;

namespace Abp.EntityFramework
Expand All @@ -29,14 +30,29 @@ public virtual void Add(Type sourceDbContextType, Type targetDbContextType)
_dbContextTypes[sourceDbContextType].Add(targetDbContextType);
}

public void Populate(Type[] dbContextTypes)
{
foreach (var dbContextType in dbContextTypes)
{
var types = new List<Type>();
AddWithBaseTypes(dbContextType, types);
foreach (var type in types)
{
Add(type, dbContextType);
}
}
}

public virtual Type GetConcreteType(Type sourceDbContextType)
{
//TODO: This can also get MultiTenancySide to filter dbcontexes

//TODO: Can be optimized by extracting/caching MultiTenancySideAttribute attributes for DbContexes.

//Get possible concrete types for given DbContext type
var targetList = _dbContextTypes.GetOrDefault(sourceDbContextType);
var allTargetTypes = _dbContextTypes.GetOrDefault(sourceDbContextType);

if (targetList.IsNullOrEmpty())
if (allTargetTypes.IsNullOrEmpty())
{
//Not found any target type, return the given type if it's not abstract
if (sourceDbContextType.IsAbstract)
Expand All @@ -47,10 +63,10 @@ public virtual Type GetConcreteType(Type sourceDbContextType)
return sourceDbContextType;
}

if (targetList.Count == 1)
if (allTargetTypes.Count == 1)
{
//Only one type does exists, return it
return targetList[0];
return allTargetTypes[0];
}

//Will decide the target type with current UOW, so it should be in a UOW.
Expand All @@ -63,35 +79,68 @@ public virtual Type GetConcreteType(Type sourceDbContextType)
? MultiTenancySides.Host
: MultiTenancySides.Tenant;

targetList = targetList.Where(type =>
var multiTenancySideContexes = allTargetTypes.Where(type =>
{
var attrs = type.GetCustomAttributes(typeof (MultiTenancySideAttribute), true);
var attrs = type.GetCustomAttributes(typeof(MultiTenancySideAttribute), true);
if (attrs.IsNullOrEmpty())
{
return false;
}
return ((MultiTenancySideAttribute) attrs[0]).Side.HasFlag(currentTenancySide);
return ((MultiTenancySideAttribute)attrs[0]).Side.HasFlag(currentTenancySide);
}).ToList();

if (targetList.Count < 1)
//Try to get the DbContext which is for current multitenancy side.
if (multiTenancySideContexes.Count == 1)
{
throw new AbpException(string.Format(
"Found more than one concrete type for given DbContext Type ({0}) but none of them defines MultiTenancySideAttribute with {1}",
sourceDbContextType,
currentTenancySide
));
return multiTenancySideContexes[0];
}
if (targetList.Count > 1)

if (multiTenancySideContexes.Count > 1)
{
//Try to get the DbContext which not defined AutoRepositoryTypesAttribute
var defaultRepositoryContexesInMultiTenancySide = multiTenancySideContexes
.Where(type => !type.IsDefined(typeof(AutoRepositoryTypesAttribute), true))
.ToList();

if (defaultRepositoryContexesInMultiTenancySide.Count == 1)
{
return defaultRepositoryContexesInMultiTenancySide[0];
}

throw new AbpException(string.Format(
"Found more than one concrete type for given DbContext Type ({0}) define MultiTenancySideAttribute with {1}",
"Found more than one concrete type for given DbContext Type ({0}) define MultiTenancySideAttribute with {1}. Found types: {2}.",
sourceDbContextType,
currentTenancySide
currentTenancySide,
multiTenancySideContexes.JoinAsString(", ")
));
}

//Try to get the DbContext which not defined AutoRepositoryTypesAttribute
var defaultRepositoryContexes = allTargetTypes
.Where(type => !type.IsDefined(typeof(AutoRepositoryTypesAttribute), true))
.ToList();

if (defaultRepositoryContexes.Count == 1)
{
return defaultRepositoryContexes[0];
}

throw new AbpException(string.Format(
"Found more than one concrete type for given DbContext Type ({0}) but none of them defines MultiTenancySideAttribute with {1}. Found types: {2}.",
sourceDbContextType,
currentTenancySide,
multiTenancySideContexes.JoinAsString(", ")
));
}

return targetList[0];
private static void AddWithBaseTypes(Type dbContextType, List<Type> types)
{
types.Add(dbContextType);
if (dbContextType != typeof(AbpDbContext))
{
AddWithBaseTypes(dbContextType.BaseType, types);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;

namespace Abp.EntityFramework
{
public interface IDbContextTypeMatcher
{
void Add(Type sourceDbContextType, Type targetDbContextType);

void Populate(Type[] dbContextTypes);

Type GetConcreteType(Type sourceDbContextType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="DbContextTypeMatcher_Tests.cs" />
<Compile Include="Repositories\EntityFrameworkGenericRepositoryRegistrar_Tests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utils\DateTimePropertyInfoFinder_Tests.cs" />
Expand Down
102 changes: 102 additions & 0 deletions src/Tests/Abp.EntityFramework.Tests/DbContextTypeMatcher_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Abp.Domain.Entities;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.EntityFramework.Repositories;
using Abp.MultiTenancy;
using Abp.Tests;
using NSubstitute;
using Shouldly;
using Xunit;

namespace Abp.EntityFramework.Tests
{
public class DbContextTypeMatcher_Tests : TestBaseWithLocalIocManager
{
private int? _tenantId = 1;

private readonly DbContextTypeMatcher matcher;

public DbContextTypeMatcher_Tests()
{
var fakeUow = Substitute.For<IUnitOfWork>();
fakeUow.GetTenantId().Returns(callInfo => _tenantId);
var fakeCurrentUowProvider = Substitute.For<ICurrentUnitOfWorkProvider>();
fakeCurrentUowProvider.Current.Returns(fakeUow);

matcher = new DbContextTypeMatcher(fakeCurrentUowProvider);
matcher.Populate(new []
{
typeof(MyDerivedDbContext1),
typeof(MyDerivedDbContext2),
typeof(MyDerivedDbContext3)
});
}

[Fact]
public void Should_Get_Same_Types_For_Defined_Non_Abstract_Types()
{
matcher.GetConcreteType(typeof(MyDerivedDbContext1)).ShouldBe(typeof(MyDerivedDbContext1));
matcher.GetConcreteType(typeof(MyDerivedDbContext2)).ShouldBe(typeof(MyDerivedDbContext2));
matcher.GetConcreteType(typeof(MyDerivedDbContext3)).ShouldBe(typeof(MyDerivedDbContext3));
}

[Fact]
public void Should_Get_Same_Types_For_Undefined_Non_Abstract_Types()
{
matcher.GetConcreteType(typeof(MyDerivedDbContextNotDefined)).ShouldBe(typeof(MyDerivedDbContextNotDefined));
}

[Fact]
public void Should_Get_Single_DbContext_For_Current_Tenancy_Side_When_BaseDbContext_Requested()
{
//Should return MyDerivedDbContext3 since it defines MultiTenancySides.Tenant
matcher.GetConcreteType(typeof(MyCommonDbContext)).ShouldBe(typeof(MyDerivedDbContext3));
}

[Fact]
public void Should_Throw_Exception_If_Multiple_DbContext_For_Current_Tenancy_Side_When_BaseDbContext_Requested()
{
_tenantId = null; //switching to host side (which have more than 1 dbcontext)
matcher.GetConcreteType(typeof(MyCommonDbContext)).ShouldBe(typeof(MyDerivedDbContext1));
}

private abstract class MyCommonDbContext : AbpDbContext
{

}

[MultiTenancySide(MultiTenancySides.Host)]
private class MyDerivedDbContext1 : MyCommonDbContext
{

}

[AutoRepositoryTypes( //Does not matter parameters for these tests
typeof(IRepository<>),
typeof(IRepository<,>),
typeof(EfRepositoryBase<,>),
typeof(EfRepositoryBase<,,>)
)]
[MultiTenancySide(MultiTenancySides.Host)]
private class MyDerivedDbContext2 : MyCommonDbContext
{

}

[MultiTenancySide(MultiTenancySides.Tenant)]
private class MyDerivedDbContext3 : MyCommonDbContext
{

}

private class MyDerivedDbContextNotDefined : MyCommonDbContext
{

}

private class MyCommonEntity : Entity
{

}
}
}

0 comments on commit 8761b8c

Please sign in to comment.