Instead of passing connection string like this you can adapt the following multi tenancy pattern.
Define Tenant
public class Tenant
{
public string Name { get; set; }
public string TID { get; set; }
public string ConnectionString { get; set; }
}
Tenant Settings
public class TenantSettings
{
public Configuration Defaults { get; set; }
public List<Tenant> Tenants { get; set; }
}
And Configuration
public class Configuration
{
public string DBProvider { get; set; }
public string ConnectionString { get; set; }
}
Your connection string structure will be following like this
"TenantSettings": {
"Defaults": {
"DBProvider": "mssql",
"ConnectionString": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=sharedTenantDb;Integrated Security=True;MultipleActiveResultSets=True"
},
"Tenants": [
{
"Name": "alpha",
"TID": "alpha",
"ConnectionString": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=alphaTenantDb;Integrated Security=True;MultipleActiveResultSets=True"
},
{
"Name": "beta",
"TID": "beta",
"ConnectionString": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=betaTenantDb;Integrated Security=True;MultipleActiveResultSets=True"
}
]
}
Our tenant service will be like this
public interface ITenantService
{
public string GetDatabaseProvider();
public string GetConnectionString();
public Tenant GetTenant();
}
Implementation
public class TenantService : ITenantService
{
private readonly TenantSettings _tenantSettings;
private HttpContext _httpContext;
private Tenant _currentTenant;
public TenantService(IOptions<TenantSettings> tenantSettings, IHttpContextAccessor contextAccessor)
{
_tenantSettings = tenantSettings.Value;
_httpContext = contextAccessor.HttpContext;
if (_httpContext != null)
{
if (_httpContext.Request.Headers.TryGetValue("tenant", out var tenantId))
{
SetTenant(tenantId);
}
else
{
throw new Exception("Invalid Tenant!");
}
}
}
private void SetTenant(string tenantId)
{
_currentTenant = _tenantSettings.Tenants.Where(a => a.TID == tenantId).FirstOrDefault();
if (_currentTenant == null) throw new Exception("Invalid Tenant!");
if (string.IsNullOrEmpty(_currentTenant.ConnectionString))
{
SetDefaultConnectionStringToCurrentTenant();
}
}
private void SetDefaultConnectionStringToCurrentTenant()
{
_currentTenant.ConnectionString = _tenantSettings.Defaults.ConnectionString;
}
public string GetConnectionString()
{
return _currentTenant?.ConnectionString;
}
public string GetDatabaseProvider()
{
return _tenantSettings.Defaults?.DBProvider;
}
public Tenant GetTenant()
{
return _currentTenant;
}
}
Extend the DBContext
public class ApplicationDbContext : DbContext
{
public string TenantId { get; set; }
private readonly ITenantService _tenantService;
public ApplicationDbContext(DbContextOptions options, ITenantService tenantService) : base(options)
{
_tenantService = tenantService;
TenantId = _tenantService.GetTenant()?.TID;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var tenantConnectionString = _tenantService.GetConnectionString();
if (!string.IsNullOrEmpty(tenantConnectionString))
{
var DBProvider = _tenantService.GetDatabaseProvider();
if (DBProvider.ToLower() == "mssql")
{
optionsBuilder.UseSqlServer(_tenantService.GetConnectionString());
}
}
}
}
There you have it, You can access your current tenant through DI (ITenantService) and have your current DBContext configured also you can do several operation depends on the TenantId .