I have multi tenant architecture build in ASP.NET Core 6, I want to create an instance of a service like MessageService which is singleton service that should be isolate for each tenant. The problem is when I did dependency injection there are 3 life time scopes which are
Singleton
Transient
Scope
For Transient and Scope it will create new instance every time request is coming, and Singleton it will create 1 time but it will mix with all tenant
My question is how can I create the instance of service that like Singleton of each tenant in asp.net core 6 using Autofac or using any other framework/Library? Or in short: I want to create tenant specific containers in ASP.NET Core 6.
Build-in DI does not support keyed dependencies/injection based on name so you have basically two options - switch to another container which does support it (for example Autofac - and my take on factories with it) or create a factory. Simple example for the second approach using Func
factory can look something like the following (assuming tenants are static for app run, dynamic can be done in similar way with small modifications):
interface ISomeTenantService
{
public int TenantId { get; } // possibly move to another interface for interface segregation
public void SomeMethod();
}
class SomeTenantServiceParams
{
// all required deps here
}
class SomeTenantService : ISomeTenantService
{
public SomeTenantService(int tenantId, SomeTenantServiceParams p)
{
TenantId = tenantId;
// flatten and assign SomeTenantServiceParams
// ...
}
public int TenantId { get; }
public void SomeMethod()
{
throw new NotImplementedException();
}
}
// registration:
int[] tenants = { 1, 2 }; // supported tenants
services.AddSingleton<SomeTenantServiceParams>();
foreach (var tenantId in tenants)
{
services.AddSingleton<ISomeTenantService>(sp =>
new SomeTenantService(tenantId, sp.GetRequiredService<SomeTenantServiceParams>()));
}
services.AddSingleton<Func<int, ISomeTenantService>>(sp =>
{
var all = sp.GetRequiredService<IEnumerable<ISomeTenantService>>();
var lookup = all.ToDictionary(s => s.TenantId);
return i => lookup[i];
});
And then you inject the Func<int, ISomeTenantService>
factory where needed and simply use it:
Func<int, ISomeTenantService> _factory = ...; // injected
var forFirstTenant = _factory(1);
UPD
Since .NET 8 keyed services are supported.