diff --git a/Volo.Abp.sln b/Volo.Abp.sln index 4fba1e3103..d6a627317f 100644 --- a/Volo.Abp.sln +++ b/Volo.Abp.sln @@ -46,6 +46,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Volo.Abp.MultiTenancy", "sr EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Volo.Abp.MultiTenancy.Tests", "test\Volo.Abp.MultiTenancy.Tests\Volo.Abp.MultiTenancy.Tests.xproj", "{05271341-7A15-484C-9FD6-802A4193F4DE}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Volo.Abp.AspNetCore.MultiTenancy", "src\Volo.Abp.AspNetCore.MultiTenancy\Volo.Abp.AspNetCore.MultiTenancy.xproj", "{7CC7946B-E026-4F66-8D4F-4F78F4801D43}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -100,6 +102,10 @@ Global {05271341-7A15-484C-9FD6-802A4193F4DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {05271341-7A15-484C-9FD6-802A4193F4DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {05271341-7A15-484C-9FD6-802A4193F4DE}.Release|Any CPU.Build.0 = Release|Any CPU + {7CC7946B-E026-4F66-8D4F-4F78F4801D43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CC7946B-E026-4F66-8D4F-4F78F4801D43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CC7946B-E026-4F66-8D4F-4F78F4801D43}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CC7946B-E026-4F66-8D4F-4F78F4801D43}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -122,5 +128,6 @@ Global {B520B696-86C7-46D2-A359-C2E9013A7BED} = {82B41A0A-6068-410F-9C6B-2508CA763E21} {58FA9F8F-216D-4C93-8929-D40D22B11CA7} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8} {05271341-7A15-484C-9FD6-802A4193F4DE} = {37087D1B-3693-4E96-983D-A69F210BDE53} + {7CC7946B-E026-4F66-8D4F-4F78F4801D43} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8} EndGlobalSection EndGlobal diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/AssemblyInfo.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e59be26506 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Volo.Abp.AspNetCore.MultiTenancy")] +[assembly: AssemblyTrademark("")] + +// 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 +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7cc7946b-e026-4f66-8d4f-4f78f4801d43")] diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo.Abp.AspNetCore.MultiTenancy.xproj b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo.Abp.AspNetCore.MultiTenancy.xproj new file mode 100644 index 0000000000..5d264e68e9 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo.Abp.AspNetCore.MultiTenancy.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 7cc7946b-e026-4f66-8d4f-4f78f4801d43 + + + .\obj + .\bin\ + v4.6.1 + + + 2.0 + + + \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/project.json b/src/Volo.Abp.AspNetCore.MultiTenancy/project.json new file mode 100644 index 0000000000..4690ae6dda --- /dev/null +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/project.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "NETStandard.Library": "1.6.1", + "Volo.Abp.MultiTenancy": "1.0.0-*" + }, + + "frameworks": { + "netstandard1.6": { + "imports": "dnxcore50" + } + } +} diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AmbientTenantAccessor.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AmbientTenantAccessor.cs new file mode 100644 index 0000000000..d7b70e1ad9 --- /dev/null +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AmbientTenantAccessor.cs @@ -0,0 +1,21 @@ +using System.Threading; +using Volo.DependencyInjection; + +namespace Volo.Abp.MultiTenancy +{ + public class AmbientTenantAccessor : IAmbientTenantAccessor, ISingletonDependency //TODO: Should be IScopedDependency? + { + public AmbientTenantInfo AmbientTenant + { + get { return _tenant.Value; } + set { _tenant.Value = value; } + } + + private readonly AsyncLocal _tenant; + + public AmbientTenantAccessor() + { + _tenant = new AsyncLocal(); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantResolveContext.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantResolveContext.cs index e68450fdee..9b0c17dfcb 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantResolveContext.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/CurrentTenantResolveContext.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.MultiTenancy { public class CurrentTenantResolveContext : ICurrentTenantResolveContext { - public ITenantInfo Tenant { get; set; } + public TenantInfo Tenant { get; set; } public bool Handled { get; set; } } diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IAmbientTenantAccessor.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IAmbientTenantAccessor.cs new file mode 100644 index 0000000000..146ad48c5c --- /dev/null +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IAmbientTenantAccessor.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.MultiTenancy +{ + public interface IAmbientTenantAccessor + { + AmbientTenantInfo AmbientTenant { get; set; } + } + + public class AmbientTenantInfo + { + /// + /// Null for host. + /// + public TenantInfo Tenant { get; set; } + + public AmbientTenantInfo([CanBeNull] TenantInfo tenant) + { + Tenant = tenant; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ICurrentTenantResolveContext.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ICurrentTenantResolveContext.cs index 61aa9273ec..73b89c1bd9 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ICurrentTenantResolveContext.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ICurrentTenantResolveContext.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.MultiTenancy { public interface ICurrentTenantResolveContext { - ITenantInfo Tenant { get; set; } + TenantInfo Tenant { get; set; } bool Handled { get; set; } } diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IMultiTenancyManager.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IMultiTenancyManager.cs index 43aa6db2e0..3b5f055c77 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IMultiTenancyManager.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IMultiTenancyManager.cs @@ -1,7 +1,11 @@ +using System; + namespace Volo.Abp.MultiTenancy { public interface IMultiTenancyManager { - ITenantInfo CurrentTenant { get; } + TenantInfo CurrentTenant { get; } + + IDisposable ChangeTenant(TenantInfo tenantInfo); } } \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantInfo.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantInfo.cs deleted file mode 100644 index eb72ef6e8f..0000000000 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Volo.Abp.MultiTenancy -{ - public interface ITenantInfo - { - string Id { get; } - - string Name { get; } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs index e2b5665985..3bf70d6b45 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs @@ -5,17 +5,24 @@ namespace Volo.Abp.MultiTenancy { public class MultiTenancyManager : IMultiTenancyManager { - public ITenantInfo CurrentTenant => GetCurrentTenant(); + public TenantInfo CurrentTenant => GetCurrentTenant(); + private readonly IAmbientTenantAccessor _ambientTenantAccessor; private readonly IEnumerable _currentTenantResolvers; - public MultiTenancyManager(IEnumerable currentTenantResolvers) + public MultiTenancyManager(IAmbientTenantAccessor ambientTenantAccessor, IEnumerable currentTenantResolvers) { + _ambientTenantAccessor = ambientTenantAccessor; _currentTenantResolvers = currentTenantResolvers; } - protected virtual ITenantInfo GetCurrentTenant() + protected virtual TenantInfo GetCurrentTenant() { + if (_ambientTenantAccessor.AmbientTenant != null) + { + return _ambientTenantAccessor.AmbientTenant.Tenant; + } + var context = new CurrentTenantResolveContext(); foreach (var currentTenantResolver in _currentTenantResolvers) @@ -29,5 +36,17 @@ namespace Volo.Abp.MultiTenancy return context.Tenant; } + + public IDisposable ChangeTenant(TenantInfo tenantInfo) + { + var oldValue = _ambientTenantAccessor.AmbientTenant; + + _ambientTenantAccessor.AmbientTenant = new AmbientTenantInfo(tenantInfo); + + return new DisposeAction(() => + { + _ambientTenantAccessor.AmbientTenant = oldValue; + }); + } } } diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs index 83a6f6c3fd..00b27bb348 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs @@ -2,7 +2,7 @@ using JetBrains.Annotations; namespace Volo.Abp.MultiTenancy { - public class TenantInfo : ITenantInfo + public class TenantInfo { public string Id { get; } diff --git a/src/Volo.Abp/Volo/Abp/DisposeAction.cs b/src/Volo.Abp/Volo/Abp/DisposeAction.cs new file mode 100644 index 0000000000..116249e58c --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/DisposeAction.cs @@ -0,0 +1,29 @@ +using System; + +namespace Volo.Abp +{ + /// + /// This class can be used to provide an action when + /// Dipose method is called. + /// + public class DisposeAction : IDisposable + { + private readonly Action _action; + + /// + /// Creates a new object. + /// + /// Action to be executed when this object is disposed. + public DisposeAction(Action action) + { + Check.NotNull(action, nameof(action)); + + _action = action; + } + + public void Dispose() + { + _action(); + } + } +} diff --git a/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_TenantResolver_Tests.cs b/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_TenantResolver_Tests.cs index 2d12040219..b8027f0e12 100644 --- a/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_TenantResolver_Tests.cs +++ b/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_TenantResolver_Tests.cs @@ -1,4 +1,5 @@ using System; +using NSubstitute; using Shouldly; using Xunit; @@ -11,9 +12,9 @@ namespace Volo.Abp.MultiTenancy { //Arrange - var manager = new MultiTenancyManager(new ITenantResolver[0]); + var manager = new MultiTenancyManager(Substitute.For(), new ITenantResolver[0]); - //Act + //Assert manager.CurrentTenant.ShouldBeNull(); } @@ -25,9 +26,7 @@ namespace Volo.Abp.MultiTenancy var fakeTenant = new TenantInfo(Guid.NewGuid().ToString(), "acme"); - //Act - - var manager = new MultiTenancyManager(new[] + var manager = new MultiTenancyManager(Substitute.For(), new[] { new TenantResolverAction(context => { @@ -49,13 +48,11 @@ namespace Volo.Abp.MultiTenancy var fakeTenant = new TenantInfo(Guid.NewGuid().ToString(), "acme"); - //Act - - var manager = new MultiTenancyManager(new[] + var manager = new MultiTenancyManager(Substitute.For(), new[] { new TenantResolverAction(context => { - context.Tenant = new TenantInfo(Guid.NewGuid().ToString(), "skipped-tenant"); + context.Tenant = new TenantInfo(Guid.NewGuid().ToString(), "skipped-tenant-1"); }), new TenantResolverAction(context => { @@ -64,7 +61,7 @@ namespace Volo.Abp.MultiTenancy }), new TenantResolverAction(context => { - context.Tenant = new TenantInfo(Guid.NewGuid().ToString(), "skipped-tenant"); + context.Tenant = new TenantInfo(Guid.NewGuid().ToString(), "skipped-tenant-2"); context.Handled = true; }) }); @@ -73,5 +70,36 @@ namespace Volo.Abp.MultiTenancy manager.CurrentTenant.ShouldBe(fakeTenant); } + + [Fact] + public void Should_Get_Ambient_Tenant_If_Changed() + { + //Arrange + + var oldTenant = new TenantInfo(Guid.NewGuid().ToString(), "old-tenant"); + + var manager = new MultiTenancyManager(Substitute.For(), new[] + { + new TenantResolverAction(context => + { + context.Tenant = oldTenant; + context.Handled = true; + }) + }); + + manager.CurrentTenant.ShouldBe(oldTenant); + + //Act + + var overridedTenant = new TenantInfo(Guid.NewGuid().ToString(), "overrided-tenant"); + using (manager.ChangeTenant(overridedTenant)) + { + //Assert + manager.CurrentTenant.ShouldBe(overridedTenant); + } + + //Assert + manager.CurrentTenant.ShouldBe(oldTenant); + } } }