TenantDistributor
Represents a tenant distributor that manages tenants in a multi-tenant environment.
using Stashbox.Utils;
using Stashbox.Utils.Data;
using Stashbox.Utils.Data.Immutable;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Stashbox.Multitenant
{
public sealed class TenantDistributor : ITenantDistributor, IAsyncDisposable, IDisposable
{
private int disposed;
private ImmutableTree<object, IStashboxContainer> tenantRepository = ImmutableTree<object, IStashboxContainer>.Empty;
public IStashboxContainer RootContainer { get; }
public TenantDistributor(IStashboxContainer rootContainer = null)
{
RootContainer = (rootContainer ?? new StashboxContainer(null));
}
public void ConfigureTenant(object tenantId, Func<IStashboxContainer, IDisposable> tenantConfig)
{
Shield.EnsureNotNull(tenantId, "tenantId");
Shield.EnsureNotNull(tenantConfig, "tenantConfig");
IStashboxContainer stashboxContainer = RootContainer.CreateChildContainer(null);
if (Swap.SwapValue<object, IStashboxContainer, byte, byte, ImmutableTree<object, IStashboxContainer>>(ref tenantRepository, (object id, IStashboxContainer container, byte _, byte _, ImmutableTree<object, IStashboxContainer> repo) => repo.AddOrUpdate(id, container, false, false), tenantId, stashboxContainer, 0, 0)) {
IDisposable disposable = tenantConfig(stashboxContainer);
RootContainer.ContainerContext.RootScope.AddDisposableTracking(disposable);
}
}
public IDependencyResolver GetTenant(object tenantId)
{
Shield.EnsureNotNull(tenantId, "tenantId");
return tenantRepository.GetOrDefaultByValue(tenantId);
}
public void Validate()
{
ExpandableArray<Exception> expandableArray = new ExpandableArray<Exception>();
try {
RootContainer.Validate();
} catch (AggregateException ex) {
expandableArray.Add(new AggregateException("Root container validation failed. See the inner exceptions for details.", ex.InnerExceptions));
}
foreach (ReadOnlyKeyValue<object, IStashboxContainer> item in tenantRepository.Walk()) {
try {
item.Value.Validate();
} catch (AggregateException ex2) {
expandableArray.Add(new AggregateException($"""{item.Key}""", ex2.InnerExceptions));
}
}
if (expandableArray.Length > 0)
throw new AggregateException("Tenant distributor validation failed. See the inner exceptions for details.", expandableArray);
}
public void Dispose()
{
if (Interlocked.CompareExchange(ref disposed, 1, 0) == 0)
RootContainer.Dispose();
}
public ValueTask DisposeAsync()
{
if (Interlocked.CompareExchange(ref disposed, 1, 0) != 0)
return new ValueTask(Task.CompletedTask);
return RootContainer.DisposeAsync();
}
}
}