From 5837a3ef4538e1e091b44974d0cac76761ba78ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 21 Jan 2017 15:30:16 +0300 Subject: [PATCH] Revised multitenancy resolve and store system. --- .../AbpDesk.ConsoleDemo/appsettings.json | 21 ++++- .../MultiTenancy/CookieTenantResolver.cs | 2 +- .../MultiTenancy/HeaderTenantResolver.cs | 2 +- .../MultiTenancy/HttpTenantResolverBase.cs | 8 +- .../MultiTenancy/QueryStringTenantResolver.cs | 2 +- .../MultiTenancy/RouteTenantResolver.cs | 2 +- .../ITenantConnectionStringStore.cs | 13 --- .../MultiTenantConnectionStringResolver.cs | 11 +-- .../NullTenantConnectionStringStore.cs | 19 ---- .../AsyncLocalTenantScopeProvider.cs | 2 +- .../ConfigurationTenantStore.cs | 28 ++++++ .../ConfigurationTenantStoreOptions.cs | 12 +++ .../Abp/MultiTenancy/IMultiTenancyManager.cs | 8 +- .../Abp/MultiTenancy/ITenantResolveContext.cs | 4 +- .../Abp/MultiTenancy/ITenantScopeProvider.cs | 5 +- .../Volo/Abp/MultiTenancy/ITenantStore.cs | 14 +++ .../Abp/MultiTenancy/MultiTenancyManager.cs | 90 ++++++++++++++++--- .../Volo/Abp/MultiTenancy/TenantInfo.cs | 22 ----- .../Abp/MultiTenancy/TenantInformation.cs | 28 ++++++ .../Abp/MultiTenancy/TenantResolveContext.cs | 8 +- .../Volo/Abp/MultiTenancy/TenantScope.cs | 4 +- src/Volo.Abp/Volo/Abp/AbpKernelModule.cs | 1 + .../Volo/Abp/Data/ConnectionStrings.cs | 3 +- .../Abp/Domain/Repositories/IRepository.cs | 1 - src/Volo.Abp/project.json | 1 + .../Volo/Abp/AspNetCore/App/AppModule.cs | 2 +- .../AspNetCoreMultiTenancy_Tests.cs | 40 ++++++--- ...ltiTenantConnectionStringResolver_Tests.cs | 52 +++++------ .../MultiTenancy/MultiTenantManager_Tests.cs | 49 +++++++--- 29 files changed, 302 insertions(+), 152 deletions(-) delete mode 100644 src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/ITenantConnectionStringStore.cs delete mode 100644 src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/NullTenantConnectionStringStore.cs create mode 100644 src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/ConfigurationTenantStore.cs create mode 100644 src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/ConfigurationTenantStoreOptions.cs create mode 100644 src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantStore.cs delete mode 100644 src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs create mode 100644 src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInformation.cs diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json b/src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json index aadbbcdae6..606e269345 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json @@ -2,5 +2,24 @@ "ConnectionStrings": { "Default": "Server=localhost;Database=AbpDesk;Trusted_Connection=True;", "AbpDeskMongoBlog": "mongodb://127.0.0.1:27017|AbpDeskBlog" - } + }, + //Example tenant configuration, not used! + "Tenants": [ + { + "Id": "2D8BF07D-50F4-4A70-805E-C7618F008043", + "Name": "Acme", + "ConnectionStrings": { + "Default": "", + "AbpDeskMongoBlog": "" + } + }, + { + "Id": "33A01BA1-4106-41DA-AF34-28028FB9BD1D", + "Name": "Vlsft", + "ConnectionStrings": { + "Default": "", + "AbpDeskMongoBlog": "" + } + } + ] } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolver.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolver.cs index cbd5b25fd2..385824e83b 100644 --- a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolver.cs +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolver.cs @@ -5,7 +5,7 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { public class CookieTenantResolver : HttpTenantResolverBase { - protected override string GetTenantIdFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) + protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) { return httpContext.Request.Cookies[context.GetAspNetCoreMultiTenancyOptions().TenantIdKey]; } diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolver.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolver.cs index d6c01ed05b..4f71e83102 100644 --- a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolver.cs +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolver.cs @@ -7,7 +7,7 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { public class HeaderTenantResolver : HttpTenantResolverBase { - protected override string GetTenantIdFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) + protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) { //TODO: Get first one if provided multiple values and write a log if (httpContext.Request.Headers.IsNullOrEmpty()) diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolverBase.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolverBase.cs index 008c7c62e3..0a379f2b2b 100644 --- a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolverBase.cs +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolverBase.cs @@ -19,13 +19,13 @@ namespace Volo.Abp.AspNetCore.MultiTenancy private void ResolveFromHttpContext(ITenantResolveContext context, HttpContext httpContext) { - var tenantId = GetTenantIdFromHttpContextOrNull(context, httpContext); - if (!tenantId.IsNullOrEmpty()) + var tenantIdOrName = GetTenantIdOrNameFromHttpContextOrNull(context, httpContext); + if (!tenantIdOrName.IsNullOrEmpty()) { - context.Tenant = new TenantInfo(tenantId); + context.TenantIdOrName = tenantIdOrName; } } - protected abstract string GetTenantIdFromHttpContextOrNull(ITenantResolveContext context,HttpContext httpContext); + protected abstract string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context,HttpContext httpContext); } } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs index d928397ac7..d662113635 100644 --- a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs @@ -5,7 +5,7 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { public class QueryStringTenantResolver : HttpTenantResolverBase { - protected override string GetTenantIdFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) + protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) { if (!httpContext.Request.QueryString.HasValue) { diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolver.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolver.cs index 73414db5f1..d730e834ac 100644 --- a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolver.cs +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolver.cs @@ -7,7 +7,7 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { public class RouteTenantResolver : HttpTenantResolverBase { - protected override string GetTenantIdFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) + protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) { var tenantId = httpContext.GetRouteValue(context.GetAspNetCoreMultiTenancyOptions().TenantIdKey); if (tenantId == null) diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/ITenantConnectionStringStore.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/ITenantConnectionStringStore.cs deleted file mode 100644 index fde063d053..0000000000 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/ITenantConnectionStringStore.cs +++ /dev/null @@ -1,13 +0,0 @@ -using JetBrains.Annotations; - -namespace Volo.Abp.Data.MultiTenancy -{ - public interface ITenantConnectionStringStore - { - [CanBeNull] - string GetDefaultConnectionStringOrNull([NotNull] string tenantId); - - [CanBeNull] - string GetConnectionStringOrNull([NotNull] string tenantId, [NotNull] string connStringName); - } -} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/MultiTenantConnectionStringResolver.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/MultiTenantConnectionStringResolver.cs index 98c02ac70e..1ac6bbfcd2 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/MultiTenantConnectionStringResolver.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/MultiTenantConnectionStringResolver.cs @@ -9,16 +9,13 @@ namespace Volo.Abp.Data.MultiTenancy public class MultiTenantConnectionStringResolver : DefaultConnectionStringResolver { private readonly IMultiTenancyManager _multiTenancyManager; - private readonly ITenantConnectionStringStore _tenantConnectionStringStore; public MultiTenantConnectionStringResolver( IOptionsSnapshot options, - IMultiTenancyManager multiTenancyManager, - ITenantConnectionStringStore tenantConnectionStringStore) + IMultiTenancyManager multiTenancyManager) : base(options) { _multiTenancyManager = multiTenancyManager; - _tenantConnectionStringStore = tenantConnectionStringStore; } public override string Resolve(string connectionStringName = null) @@ -34,12 +31,12 @@ namespace Volo.Abp.Data.MultiTenancy //Requesting default connection string if (connectionStringName == null) { - return _tenantConnectionStringStore.GetDefaultConnectionStringOrNull(tenant.Id) ?? + return tenant.ConnectionStrings.Default ?? Options.ConnectionStrings.Default; } //Requesting specific connection string - var connString = _tenantConnectionStringStore.GetConnectionStringOrNull(tenant.Id, connectionStringName); + var connString = tenant.ConnectionStrings.GetOrDefault(connectionStringName); if (connString != null) { return connString; @@ -56,7 +53,7 @@ namespace Volo.Abp.Data.MultiTenancy return connStringInOptions; } - return _tenantConnectionStringStore.GetDefaultConnectionStringOrNull(tenant.Id) ?? + return tenant.ConnectionStrings.Default ?? Options.ConnectionStrings.Default; } } diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/NullTenantConnectionStringStore.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/NullTenantConnectionStringStore.cs deleted file mode 100644 index 5762b305e4..0000000000 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/Data/MultiTenancy/NullTenantConnectionStringStore.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Volo.DependencyInjection; - -namespace Volo.Abp.Data.MultiTenancy -{ - public sealed class NullTenantConnectionStringStore : ITenantConnectionStringStore, ISingletonDependency - { - public string GetDefaultConnectionStringOrNull(string tenantId) - { - //No tenant specific connection string by default - return null; - } - - public string GetConnectionStringOrNull(string tenantId, string connStringName) - { - //No tenant specific connection string by default - return null; - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AsyncLocalTenantScopeProvider.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AsyncLocalTenantScopeProvider.cs index 06dfb84c09..e528357b72 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AsyncLocalTenantScopeProvider.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AsyncLocalTenantScopeProvider.cs @@ -19,7 +19,7 @@ namespace Volo.Abp.MultiTenancy _currentScope = new AsyncLocal(); } - public IDisposable EnterScope(TenantInfo tenant) + public IDisposable EnterScope(TenantInformation tenant) { var parentScope = CurrentScope; CurrentScope = new TenantScope(tenant); diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/ConfigurationTenantStore.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/ConfigurationTenantStore.cs new file mode 100644 index 0000000000..1c2a985f53 --- /dev/null +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/ConfigurationTenantStore.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq; +using Microsoft.Extensions.Options; +using Volo.DependencyInjection; + +namespace Volo.Abp.MultiTenancy.ConfigurationStore +{ + [Dependency(TryRegister = true)] + public class ConfigurationTenantStore : ITenantStore, ITransientDependency + { + private readonly ConfigurationTenantStoreOptions _options; + + public ConfigurationTenantStore(IOptionsSnapshot options) + { + _options = options.Value; + } + + public TenantInformation Find(string name) + { + return _options.Tenants.FirstOrDefault(t => t.Name == name); + } + + public TenantInformation Find(Guid id) + { + return _options.Tenants.FirstOrDefault(t => t.Id == id); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/ConfigurationTenantStoreOptions.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/ConfigurationTenantStoreOptions.cs new file mode 100644 index 0000000000..67a9b6bd4c --- /dev/null +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ConfigurationStore/ConfigurationTenantStoreOptions.cs @@ -0,0 +1,12 @@ +namespace Volo.Abp.MultiTenancy.ConfigurationStore +{ + public class ConfigurationTenantStoreOptions + { + public TenantInformation[] Tenants { get; set; } + + public ConfigurationTenantStoreOptions() + { + Tenants = new TenantInformation[0]; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IMultiTenancyManager.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IMultiTenancyManager.cs index eab07314e3..b69e903801 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IMultiTenancyManager.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/IMultiTenancyManager.cs @@ -6,8 +6,12 @@ namespace Volo.Abp.MultiTenancy public interface IMultiTenancyManager { [CanBeNull] - TenantInfo CurrentTenant { get; } + TenantInformation CurrentTenant { get; } - IDisposable ChangeTenant([CanBeNull] TenantInfo tenant); + IDisposable ChangeTenant(Guid? tenantId); + + IDisposable ChangeTenant([CanBeNull] string name); + + IDisposable ChangeToHost(); } } \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveContext.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveContext.cs index 1b22fff375..d4b2512d45 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveContext.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantResolveContext.cs @@ -6,8 +6,8 @@ namespace Volo.Abp.MultiTenancy public interface ITenantResolveContext : IServiceProviderAccessor { [CanBeNull] - TenantInfo Tenant { get; set; } + string TenantIdOrName { get; set; } - bool? Handled { get; set; } + bool Handled { get; set; } } } \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantScopeProvider.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantScopeProvider.cs index 03ecb1965f..e916f12b99 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantScopeProvider.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantScopeProvider.cs @@ -5,9 +5,12 @@ namespace Volo.Abp.MultiTenancy { public interface ITenantScopeProvider { + /// + /// Can be null for host. + /// [CanBeNull] TenantScope CurrentScope { get; } - IDisposable EnterScope([CanBeNull] TenantInfo tenant); + IDisposable EnterScope([CanBeNull] TenantInformation tenant); } } \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantStore.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantStore.cs new file mode 100644 index 0000000000..6bd2e363d1 --- /dev/null +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/ITenantStore.cs @@ -0,0 +1,14 @@ +using System; +using JetBrains.Annotations; + +namespace Volo.Abp.MultiTenancy +{ + public interface ITenantStore + { + [CanBeNull] + TenantInformation Find(string name); + + [CanBeNull] + TenantInformation Find(Guid id); + } +} \ 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 1d89e28b6c..f99f03531c 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs @@ -1,6 +1,8 @@ using System; using System.Linq; +using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Volo.DependencyInjection; @@ -8,23 +10,66 @@ namespace Volo.Abp.MultiTenancy { public class MultiTenancyManager : IMultiTenancyManager, ITransientDependency { - public TenantInfo CurrentTenant => GetCurrentTenant(); + public TenantInformation CurrentTenant => GetCurrentTenant(); private readonly IServiceProvider _serviceProvider; private readonly ITenantScopeProvider _tenantScopeProvider; + private readonly ITenantStore _tenantStore; private readonly MultiTenancyOptions _options; + private readonly ILogger _logger; public MultiTenancyManager( IServiceProvider serviceProvider, ITenantScopeProvider tenantScopeProvider, - IOptions options) + IOptions options, + ITenantStore tenantStore, + ILogger logger) { _serviceProvider = serviceProvider; _tenantScopeProvider = tenantScopeProvider; + _tenantStore = tenantStore; + _logger = logger; _options = options.Value; } - protected virtual TenantInfo GetCurrentTenant() + public IDisposable ChangeTenant(Guid? tenantId) + { + if (tenantId == null) + { + return ChangeToHost(); + } + + var tenant = _tenantStore.Find(tenantId.Value); + if (tenant == null) + { + throw new AbpException("There is no tenant with given tenant id: " + tenantId.Value); + } + + return _tenantScopeProvider.EnterScope(tenant); + } + + public IDisposable ChangeTenant(string name) + { + if (name == null) + { + return ChangeToHost(); + } + + var tenant = _tenantStore.Find(name); + if (tenant == null) + { + throw new AbpException("There is no tenant with given tenant name: " + name); + } + + return _tenantScopeProvider.EnterScope(tenant); + } + + public IDisposable ChangeToHost() + { + return _tenantScopeProvider.EnterScope(null); + } + + protected virtual TenantInformation GetCurrentTenant() { if (_tenantScopeProvider.CurrentScope != null) { @@ -34,8 +79,10 @@ namespace Volo.Abp.MultiTenancy return GetCurrentTenantFromResolvers(); } - protected virtual TenantInfo GetCurrentTenantFromResolvers() + protected virtual TenantInformation GetCurrentTenantFromResolvers() { + //TODO: Can be optimized by some caching mechanism? + if (!_options.TenantResolvers.Any()) { return null; @@ -49,23 +96,42 @@ namespace Volo.Abp.MultiTenancy { tenantResolver.Resolve(context); - //TODO: Validate TenantId by a TenantStore (TenantId can be TenancyName, so also normalize it by tenant store) - - if (context.IsHandled()) + if (context.ResolvedTenantOrHost()) { - break; + if (context.TenantIdOrName == null) + { + //Resolved host! + return null; + } + + var tenant = GetValidatedTenantOrNull(context.TenantIdOrName); + if (tenant != null) + { + return tenant; + } + + _logger.LogWarning($"Resolved tenancy name '{context.TenantIdOrName}' by '{tenantResolver.GetType().FullName}' but could not find in current tenant store."); + context.TenantIdOrName = null; } - context.Handled = null; + context.Handled = false; } - return context.Tenant; + //Could not find a tenant + return null; } } - public IDisposable ChangeTenant(TenantInfo tenant) + [CanBeNull] + private TenantInformation GetValidatedTenantOrNull([NotNull] string tenantIdOrName) { - return _tenantScopeProvider.EnterScope(tenant); + Guid tenantId; + if (Guid.TryParse(tenantIdOrName, out tenantId)) + { + return _tenantStore.Find(tenantId); + } + + return _tenantStore.Find(tenantIdOrName); } } } diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs deleted file mode 100644 index 53ba8529e1..0000000000 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using JetBrains.Annotations; - -namespace Volo.Abp.MultiTenancy -{ - public class TenantInfo - { - [NotNull] - public string Id { get; } - - private TenantInfo() - { - - } - - public TenantInfo([NotNull] string id) - { - Check.NotNull(id, nameof(id)); - - Id = id; - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInformation.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInformation.cs new file mode 100644 index 0000000000..b109c35ad2 --- /dev/null +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantInformation.cs @@ -0,0 +1,28 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.Data; + +namespace Volo.Abp.MultiTenancy +{ + public class TenantInformation + { + public Guid Id { get; protected set; } + + public string Name { get; protected set; } + + public ConnectionStrings ConnectionStrings { get; protected set; } + + protected TenantInformation() + { + + } + + public TenantInformation(Guid id, [NotNull] string name) + { + Id = id; + Name = name; + + ConnectionStrings = new ConnectionStrings(); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolveContext.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolveContext.cs index a44e758f54..6f84d5ed75 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolveContext.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolveContext.cs @@ -6,13 +6,13 @@ namespace Volo.Abp.MultiTenancy { public IServiceProvider ServiceProvider { get; } - public TenantInfo Tenant { get; set; } + public string TenantIdOrName { get; set; } - public bool? Handled { get; set; } + public bool Handled { get; set; } - internal bool IsHandled() + internal bool ResolvedTenantOrHost() { - return Handled == true || (Handled == null && Tenant != null); + return Handled || TenantIdOrName != null; } public TenantResolveContext(IServiceProvider serviceProvider) diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantScope.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantScope.cs index 7476ac0a1f..fdb1411c00 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantScope.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantScope.cs @@ -9,9 +9,9 @@ namespace Volo.Abp.MultiTenancy /// Not null value for a tenant. /// [CanBeNull] - public TenantInfo Tenant { get; } + public TenantInformation Tenant { get; } - public TenantScope([CanBeNull] TenantInfo tenant) + public TenantScope([CanBeNull] TenantInformation tenant) { Tenant = tenant; } diff --git a/src/Volo.Abp/Volo/Abp/AbpKernelModule.cs b/src/Volo.Abp/Volo/Abp/AbpKernelModule.cs index ca58482d9c..e96d7172db 100644 --- a/src/Volo.Abp/Volo/Abp/AbpKernelModule.cs +++ b/src/Volo.Abp/Volo/Abp/AbpKernelModule.cs @@ -8,6 +8,7 @@ namespace Volo.Abp public override void ConfigureServices(IServiceCollection services) { services.AddOptions(); + services.AddLogging(); services.AddAssemblyOf(); } diff --git a/src/Volo.Abp/Volo/Abp/Data/ConnectionStrings.cs b/src/Volo.Abp/Volo/Abp/Data/ConnectionStrings.cs index ab602bcafc..d2df90c809 100644 --- a/src/Volo.Abp/Volo/Abp/Data/ConnectionStrings.cs +++ b/src/Volo.Abp/Volo/Abp/Data/ConnectionStrings.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Volo.ExtensionMethods.Collections.Generic; namespace Volo.Abp.Data { @@ -8,7 +9,7 @@ namespace Volo.Abp.Data public string Default { - get { return this[DefaultConnectionStringName]; } + get { return this.GetOrDefault(DefaultConnectionStringName); } set { this[DefaultConnectionStringName] = value; } } } diff --git a/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs b/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs index c086c3223d..23a1361ba4 100644 --- a/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs +++ b/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs @@ -67,7 +67,6 @@ namespace Volo.Abp.Domain.Repositories [NotNull] TEntity Insert([NotNull] TEntity entity, bool autoSave = false); - //TODO: Consider to add a new overload that takes cancellationToken as second argument? /// /// Inserts a new entity. /// diff --git a/src/Volo.Abp/project.json b/src/Volo.Abp/project.json index 32a6a7341d..b3997630ff 100644 --- a/src/Volo.Abp/project.json +++ b/src/Volo.Abp/project.json @@ -5,6 +5,7 @@ "NETStandard.Library": "1.6.1", "Microsoft.Extensions.DependencyInjection": "1.1.0", "Microsoft.Extensions.Options": "1.1.0", + "Microsoft.Extensions.Logging": "1.1.0", "System.Collections.Immutable": "1.3.0", "System.Runtime.Loader": "4.3.0", "System.Linq.Queryable": "4.3.0", diff --git a/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs b/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs index f1ab08739b..5d47bc4d80 100644 --- a/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs +++ b/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs @@ -28,7 +28,7 @@ namespace Volo.Abp.AspNetCore.App var dictionary = new Dictionary { - ["TenantId"] = manager.CurrentTenant?.Id ?? "" + ["TenantId"] = manager.CurrentTenant == null ? "" : manager.CurrentTenant.Id.ToString() }; var result = jsonSerializer.Serialize(dictionary); diff --git a/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs b/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs index 2e665f2ba8..76008443ca 100644 --- a/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs +++ b/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs @@ -1,16 +1,23 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; using Shouldly; using Volo.Abp.AspNetCore.App; +using Volo.Abp.MultiTenancy; +using Volo.Abp.MultiTenancy.ConfigurationStore; using Xunit; namespace Volo.Abp.AspNetCore.MultiTenancy { public class AspNetCoreMultiTenancy_Tests : AppTestBase { + private readonly Guid _testTenantId = Guid.NewGuid(); + private readonly string _testTenantName = "acme"; + private readonly AspNetCoreMultiTenancyOptions _options; public AspNetCoreMultiTenancy_Tests() @@ -18,6 +25,20 @@ namespace Volo.Abp.AspNetCore.MultiTenancy _options = ServiceProvider.GetRequiredService>().Value; } + protected override IWebHostBuilder CreateWebHostBuilder() + { + return base.CreateWebHostBuilder().ConfigureServices(services => + { + services.Configure(options => + { + options.Tenants = new[] + { + new TenantInformation(_testTenantId, _testTenantName) + }; + }); + }); + } + [Fact] public async Task Should_Use_Host_If_Tenant_Is_Not_Specified() { @@ -28,32 +49,27 @@ namespace Volo.Abp.AspNetCore.MultiTenancy [Fact] public async Task Should_Use_QueryString_Tenant_Id_If_Specified() { - const string testTenantId = "42"; - var result = await GetResponseAsObjectAsync>($"http://abp.io?{_options.TenantIdKey}={testTenantId}"); - result["TenantId"].ShouldBe(testTenantId); + var result = await GetResponseAsObjectAsync>($"http://abp.io?{_options.TenantIdKey}={_testTenantName}"); + result["TenantId"].ShouldBe(_testTenantId.ToString()); } [Fact] public async Task Should_Use_Header_Tenant_Id_If_Specified() { - const string testTenantId = "42"; - - Client.DefaultRequestHeaders.Add(_options.TenantIdKey, testTenantId); + Client.DefaultRequestHeaders.Add(_options.TenantIdKey, _testTenantId.ToString()); var result = await GetResponseAsObjectAsync>("http://abp.io"); - result["TenantId"].ShouldBe(testTenantId); + result["TenantId"].ShouldBe(_testTenantId.ToString()); } [Fact] public async Task Should_Use_Cookie_Tenant_Id_If_Specified() { - const string testTenantId = "42"; - - Client.DefaultRequestHeaders.Add("Cookie", new CookieHeaderValue(_options.TenantIdKey, testTenantId).ToString()); + Client.DefaultRequestHeaders.Add("Cookie", new CookieHeaderValue(_options.TenantIdKey, _testTenantId.ToString()).ToString()); var result = await GetResponseAsObjectAsync>("http://abp.io"); - result["TenantId"].ShouldBe(testTenantId); + result["TenantId"].ShouldBe(_testTenantId.ToString()); } } } diff --git a/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/Data/MultiTenancy/MultiTenantConnectionStringResolver_Tests.cs b/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/Data/MultiTenancy/MultiTenantConnectionStringResolver_Tests.cs index d624419a13..e53d43ca8b 100644 --- a/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/Data/MultiTenancy/MultiTenantConnectionStringResolver_Tests.cs +++ b/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/Data/MultiTenancy/MultiTenantConnectionStringResolver_Tests.cs @@ -1,8 +1,13 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Shouldly; +using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; +using Volo.Abp.MultiTenancy.ConfigurationStore; using Volo.ExtensionMethods.Collections.Generic; using Xunit; @@ -27,14 +32,23 @@ namespace Volo.Abp.Data.MultiTenancy { options.ConnectionStrings.Default = "default-value"; options.ConnectionStrings["db1"] = "db1-default-value"; - options.ConnectionStrings["tenant1#Default"] = "tenant1-default-value"; - options.ConnectionStrings["tenant1#db1"] = "tenant1-db1-value"; }); - } - protected override void AfterAddApplication(IServiceCollection services) - { - services.Replace(ServiceDescriptor.Transient()); + services.Configure(options => + { + options.Tenants = new[] + { + new TenantInformation(Guid.NewGuid(), "tenant1") + { + ConnectionStrings = + { + { ConnectionStrings.DefaultConnectionStringName, "tenant1-default-value" }, + { "db1", "tenant1-db1-value" } + } + }, + new TenantInformation(Guid.NewGuid(), "tenant2") + }; + }); } [Fact] @@ -45,7 +59,7 @@ namespace Volo.Abp.Data.MultiTenancy _connectionResolver.Resolve("db1").ShouldBe("db1-default-value"); //Overrided connection strings for tenant1 - using (_multiTenancyManager.ChangeTenant(new TenantInfo("tenant1"))) + using (_multiTenancyManager.ChangeTenant("tenant1")) { _connectionResolver.Resolve().ShouldBe("tenant1-default-value"); _connectionResolver.Resolve("db1").ShouldBe("tenant1-db1-value"); @@ -56,31 +70,11 @@ namespace Volo.Abp.Data.MultiTenancy _connectionResolver.Resolve("db1").ShouldBe("db1-default-value"); //Undefined connection strings for tenant2 - using (_multiTenancyManager.ChangeTenant(new TenantInfo("tenant2"))) + using (_multiTenancyManager.ChangeTenant("tenant2")) { _connectionResolver.Resolve().ShouldBe("default-value"); _connectionResolver.Resolve("db1").ShouldBe("db1-default-value"); } } - - public class MyTenantConnectionStringStore : ITenantConnectionStringStore - { - private readonly IOptions _options; - - public MyTenantConnectionStringStore(IOptions options) - { - _options = options; - } - - public string GetConnectionStringOrNull(string tenantId, string connStringName) - { - return _options.Value.ConnectionStrings.GetOrDefault(tenantId + "#" + connStringName); - } - - public string GetDefaultConnectionStringOrNull(string tenantId) - { - return _options.Value.ConnectionStrings.GetOrDefault(tenantId + "#Default"); - } - } } } diff --git a/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_Tests.cs b/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_Tests.cs index 12c223ab04..0b427cf245 100644 --- a/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_Tests.cs +++ b/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/MultiTenantManager_Tests.cs @@ -1,5 +1,8 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using Microsoft.Extensions.DependencyInjection; using Shouldly; +using Volo.Abp.Data; +using Volo.Abp.MultiTenancy.ConfigurationStore; using Xunit; namespace Volo.Abp.MultiTenancy @@ -8,9 +11,9 @@ namespace Volo.Abp.MultiTenancy { private readonly IMultiTenancyManager _multiTenancyManager; - private readonly TenantInfo _tenantA = new TenantInfo("A"); - private readonly TenantInfo _tenantB = new TenantInfo("B"); - private TenantInfo _tenantToBeResolved; + private readonly string _tenantA = "A"; + private readonly string _tenantB = "B"; + private string _tenantToBeResolved; public MultiTenantManager_Tests() { @@ -25,6 +28,18 @@ namespace Volo.Abp.MultiTenancy _multiTenancyManager.CurrentTenant.ShouldBeNull(); } + protected override void BeforeAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.Tenants = new[] + { + new TenantInformation(Guid.NewGuid(), _tenantA), + new TenantInformation(Guid.NewGuid(), _tenantB) + }; + }); + } + protected override void AfterAddApplication(IServiceCollection services) { services.Configure(options => @@ -33,7 +48,7 @@ namespace Volo.Abp.MultiTenancy { if (_tenantToBeResolved == _tenantA) { - context.Tenant = _tenantA; + context.TenantIdOrName = _tenantA; } })); @@ -41,7 +56,7 @@ namespace Volo.Abp.MultiTenancy { if (_tenantToBeResolved == _tenantB) { - context.Tenant = _tenantB; + context.TenantIdOrName = _tenantB; } })); }); @@ -56,10 +71,10 @@ namespace Volo.Abp.MultiTenancy //Assert - _multiTenancyManager.CurrentTenant.ShouldBe(_tenantA); + Assert.NotNull(_multiTenancyManager.CurrentTenant); + _multiTenancyManager.CurrentTenant.Name.ShouldBe(_tenantA); } - [Fact] public void Should_Get_Current_Tenant_From_Multiple_Resolvers() { @@ -69,7 +84,8 @@ namespace Volo.Abp.MultiTenancy //Assert - _multiTenancyManager.CurrentTenant.ShouldBe(_tenantB); + Assert.NotNull(_multiTenancyManager.CurrentTenant); + _multiTenancyManager.CurrentTenant.Name.ShouldBe(_tenantB); } [Fact] @@ -79,21 +95,26 @@ namespace Volo.Abp.MultiTenancy _tenantToBeResolved = _tenantB; - _multiTenancyManager.CurrentTenant.ShouldBe(_tenantB); + Assert.NotNull(_multiTenancyManager.CurrentTenant); + _multiTenancyManager.CurrentTenant.Name.ShouldBe(_tenantB); using (_multiTenancyManager.ChangeTenant(_tenantA)) { - _multiTenancyManager.CurrentTenant.ShouldBe(_tenantA); + Assert.NotNull(_multiTenancyManager.CurrentTenant); + _multiTenancyManager.CurrentTenant.Name.ShouldBe(_tenantA); using (_multiTenancyManager.ChangeTenant(_tenantB)) { - _multiTenancyManager.CurrentTenant.ShouldBe(_tenantB); + Assert.NotNull(_multiTenancyManager.CurrentTenant); + _multiTenancyManager.CurrentTenant.Name.ShouldBe(_tenantB); } - _multiTenancyManager.CurrentTenant.ShouldBe(_tenantA); + Assert.NotNull(_multiTenancyManager.CurrentTenant); + _multiTenancyManager.CurrentTenant.Name.ShouldBe(_tenantA); } - _multiTenancyManager.CurrentTenant.ShouldBe(_tenantB); + Assert.NotNull(_multiTenancyManager.CurrentTenant); + _multiTenancyManager.CurrentTenant.Name.ShouldBe(_tenantB); } } }