I have a multitenant application on a micro service architecture design.
I want to inject X number of services, depending on the number of tenants running.
public void ConfigureServices(IServiceCollection services)
{
// ... OTHER DI
services.AddHttpClient("TenantsService")
.AddTypedClient<ITenantServiceClient>(c => new TenantServiceClient(new TenantServiceClientSettings()
{
AccessKey = Configuration["TenantsService:ApiKey"],
BaseUrl = new Uri(Configuration["TenantsService:Url"])
}, c));
foreach (var tenant in TenantsToRegister)
{
services
.AddGraphQLServer($"{tenant.Name}");
}
...
}
The above code would work if I had the list of tenants when the application starts. But I need to request that list from another microservice. Having this constraint, I need to build the service provider in order to get that list. At the same time, I need the list before the service provider's build to inject the services I need.
The only option that I see is adding the services at runtime, but I'm not sure if it's possible.
From a architectual point of view I would recommend to use "real" services for each tenant. For example start a docker or a process of your application per tenant. Like written in a former answer (thanks to sergey).
If you really want to start a new GraphQLServer for each tenant in the same process then I would do it like this: Full example on dotnet fiddle
services
.AddHttpClient("TenantsService")
.AddTypedClient<ITenantServiceClient>(c =>
new TenantServiceClient(new TenantServiceClientSettings
{
AccessKey = "THE KEY", // Configuration["TenantsService:ApiKey"],
BaseUrl = new Uri("https://the-uri-you-need.com") // new Uri(Configuration["TenantsService:Url"])
}, c));
// build a temporary service provider before, with all services added until now.
var tempServiceProvider = services.BuildServiceProvider();
// resolve the tenant service and query for tenants
var tenantsToRegister = tempServiceProvider.GetRequiredService<ITenantServiceClient>().GetTenants();
// register needed tenants
foreach (var tenant in tenantsToRegister)
{
services.AddGraphQLServer($"{tenant}");
}
... of course the whole error handling around the call to the ITenantServiceClient
must be added but its just for demo purpose.
Here the special thing is the intermediate IServiceProvider
which gives you the ability to use the full set of DI to query for your tenants but let you add the needed ones afterwards.
BUT: This is also on startup and no additional servers can be added while runtime.
Update Here is the updated dotnet fiddle (thanks to joao-figueira)