Skip to content

Commit ce6bbdb

Browse files
committed
Use Stashbox 5.17.0 / Add service collection configuration option to multitenant
1 parent 4f373f3 commit ce6bbdb

File tree

5 files changed

+106
-13
lines changed

5 files changed

+106
-13
lines changed

README.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,27 @@ var builder = WebApplication.CreateBuilder(args);
9292
builder.Host.UseStashboxMultitenant<HttpHeaderTenantIdExtractor>(
9393
options => // Multi-tenant configuration options.
9494
{
95+
// Root container configuration through the IStashboxContainer interface.
96+
options.RootContainer.Configure(opts => { /* configure the root container */ });
97+
9598
// The default service registration, it registers into the root container.
9699
// It also could be registered into the default
97100
// service collection with the ConfigureServices() API.
98-
options.RootContainer.Register<IDependency, DefaultDependency>();
101+
options.ConfigureRootServices(services =>
102+
services.AddTransient<IDependency, DefaultDependency>());
99103

100104
// Configure tenants.
101-
options.ConfigureTenant("TenantA", tenant =>
102-
// Register tenant specific service override
103-
tenant.Register<IDependency, TenantASpecificDependency>());
104-
105-
options.ConfigureTenant("TenantB", tenant =>
106-
// Register tenant specific service override
107-
tenant.Register<IDependency, TenantBSpecificDependency>());
105+
options.ConfigureTenant("TenantA", tenantContainer =>
106+
tenantContainer.Configure(opts => { /* configure the tenant container */ }))
107+
// Register tenant specific service overrides
108+
.ConfigureServices(services =>
109+
services.AddTransient<IDependency, TenantASpecificDependency>());
110+
111+
options.ConfigureTenant("TenantB", tenantContainer =>
112+
tenantContainer.Configure(opts => { /* configure the tenant container */ }))
113+
// Register tenant specific service overrides
114+
.ConfigureServices(services =>
115+
services.AddTransient<IDependency, TenantBSpecificDependency>());
108116
});
109117

110118
// The container parameter is the tenant distributor itself.
@@ -117,7 +125,6 @@ builder.Host.ConfigureContainer<IStashboxContainer>((context, container) =>
117125
});
118126
```
119127

120-
121128
With this example setup, you can differentiate tenants in a per-request basis identified by a HTTP header, where every tenant gets their overridden services.
122129

123130
### Testing

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
environment:
2-
build_version: 5.6.0
2+
build_version: 5.7.0
33

44
version: $(build_version)-{build}
55

src/stashbox.aspnetcore.multitenant/StashboxMultitenantOptions.cs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using Microsoft.Extensions.DependencyInjection;
23

34
namespace Stashbox.AspNetCore.Multitenant;
45

@@ -27,8 +28,43 @@ public StashboxMultitenantOptions(IStashboxContainer rootContainer)
2728
/// <param name="tenantId">The identifier of the tenant.</param>
2829
/// <param name="tenantConfig">The service configuration of the tenant.</param>
2930
/// <param name="attachTenantToRoot">If true, the new tenant will be attached to the lifecycle of the root container. When the root is being disposed, the tenant will be disposed with it.</param>
30-
public void ConfigureTenant(object tenantId, Action<IStashboxContainer> tenantConfig, bool attachTenantToRoot = true)
31+
/// <returns>A service configurator used to configure services for the tenant.</returns>
32+
public ITenantServiceConfigurator ConfigureTenant(object tenantId, Action<IStashboxContainer>? tenantConfig = null, bool attachTenantToRoot = true)
3133
{
32-
this.RootContainer.CreateChildContainer(tenantId, tenantConfig, attachTenantToRoot);
34+
var child = this.RootContainer.CreateChildContainer(tenantId, tenantConfig, attachTenantToRoot);
35+
return new TenantServiceConfigurator(child);
36+
}
37+
38+
/// <summary>
39+
/// Registers services into the root container from a <see cref="IServiceCollection"/>
40+
/// </summary>
41+
/// <param name="configuration">The configuration delegate.</param>
42+
public void ConfigureRootServices(Action<IServiceCollection> configuration)
43+
{
44+
var collection = new ServiceCollection();
45+
configuration(collection);
46+
this.RootContainer.RegisterServiceDescriptors(collection);
47+
}
48+
}
49+
50+
/// <summary>
51+
/// Describes a utility class to register services into a tenant container from a <see cref="IServiceCollection"/>.
52+
/// </summary>
53+
public interface ITenantServiceConfigurator
54+
{
55+
/// <summary>
56+
/// Registers services into the tenant from a <see cref="IServiceCollection"/>
57+
/// </summary>
58+
/// <param name="configuration">The configuration delegate.</param>
59+
void ConfigureServices(Action<IServiceCollection> configuration);
60+
}
61+
62+
internal class TenantServiceConfigurator(IStashboxContainer tenantContainer) : ITenantServiceConfigurator
63+
{
64+
public void ConfigureServices(Action<IServiceCollection> configuration)
65+
{
66+
var collection = new ServiceCollection();
67+
configuration(collection);
68+
tenantContainer.RegisterServiceDescriptors(collection);
3369
}
3470
}

src/stashbox.extensions.dependencyinjection/stashbox.extensions.dependencyinjection.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262

6363
<ItemGroup>
6464
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
65-
<PackageReference Include="Stashbox" Version="5.16.0" />
65+
<PackageReference Include="Stashbox" Version="5.17.0" />
6666
</ItemGroup>
6767

6868
<ItemGroup>

test/stashbox.aspnetcore.multitenant.tests/MultitenantTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
using Microsoft.Extensions.DependencyInjection;
77
using Microsoft.Extensions.Hosting;
88
using System;
9+
using System.Collections.Generic;
910
using System.Linq;
1011
using System.Net.Http;
1112
using System.Threading;
1213
using System.Threading.Tasks;
14+
using Microsoft.Extensions.Options;
15+
using Stashbox.Resolution;
1316
using Xunit;
1417

1518
namespace Stashbox.AspNetCore.Multitenant.Tests;
@@ -103,6 +106,30 @@ public async Task MultitenantTests_Works_With_ScopeFactory()
103106
Assert.True(configureCalled);
104107
Assert.True(TestStartup.ConfigureCalled);
105108
}
109+
110+
[Fact]
111+
public async Task MultitenantTests_OptionsFactory_Works()
112+
{
113+
using var host = await new HostBuilder()
114+
.UseStashboxMultitenant<TestTenantIdExtractor>(c =>
115+
{
116+
c.ConfigureRootServices(services => services.Configure<TestOption>(opt => opt.Prop = "FromRoot"));
117+
c.ConfigureTenant("A")
118+
.ConfigureServices(services => services.Configure<TestOption>(opt => opt.Prop = "FromA"));
119+
})
120+
.ConfigureWebHost(builder =>
121+
{
122+
builder
123+
.UseTestServer()
124+
.UseStartup<TestStartup>();
125+
})
126+
.StartAsync();
127+
128+
var client = host.GetTestClient();
129+
130+
await this.AssertResult(client,"api/test3/value", "NONEXISTING", "FromRoot");
131+
await this.AssertResult(client,"api/test3/value", "A", "FromA");
132+
}
106133

107134
private async Task AssertResult(HttpClient client, string route, string tenantId, string expectedResult)
108135
{
@@ -149,6 +176,24 @@ public string GetValue()
149176
}
150177
}
151178

179+
[Route("api/test3")]
180+
public class Test3Controller : ControllerBase
181+
{
182+
private readonly IOptionsMonitor<TestOption> optionsMonitor;
183+
184+
public Test3Controller(IOptionsMonitor<TestOption> optionsMonitor)
185+
{
186+
this.optionsMonitor = optionsMonitor;
187+
}
188+
189+
[HttpGet("value")]
190+
public string GetValue()
191+
{
192+
var option = this.optionsMonitor.Get(null);
193+
return option.Prop;
194+
}
195+
}
196+
152197
public class TestTenantIdExtractor : ITenantIdExtractor
153198
{
154199
public const string TENANT_HEADER = "TENANT-ID";
@@ -225,4 +270,9 @@ public ValueTask DisposeAsync()
225270

226271
return new ValueTask(Task.CompletedTask);
227272
}
273+
}
274+
275+
public class TestOption
276+
{
277+
public string Prop { get; set; }
228278
}

0 commit comments

Comments
 (0)