I am creating a backend service using ASP.NET5 Web API and EF7 to set up a multi-tenant database structure. The requirements are as follows:
- API endpoints are the same for all tenants,
- each tenant has its own database containing specific data of the tenant,
- all tenants use the same database structure, therefore the same DbContext classes can be used for all tenants, but with different connection strings,
- each tenant database contains user information for authentication based on an ASP.NET identifier.
At the moment, I have encountered the following problems:
- DbContext instances are not thread safe, so their life cycle should be short. This means that I cannot just store DbContexts instances somewhere, but dynamically create and delete instances if necessary,
- It should be possible to add or remove tenants dynamically, preferably without restarting the service,
- You must complete the migration of EF7.
To enable the service for dynamically adding or removing tenants, my current implementation is based on the JSON configuration file, which contains all tenant connection strings in key-value pairs, for example:
{
"Tenants": [
{ "Tenant1": "Server=.\\SQLEXPRESS;Database=Tenant1;integrated security=True;" },
{ "Tenant2": "Server=.\\SQLEXPRESS;Database=Tenant2;integrated security=True;" }
]
}
ContextFactory. factory DbContextOptions DbContext , , . factory :
public class TenantContextFactory : ITenantContextFactory
{
private IDictionary<string, DbContextOptions> tenants;
public TenantContextFactory()
{
tenants = new Dictionary<string, DbContextOptions>();
}
public void RegisterTenant(string id, DbContextOptions options)
{
if (!tenants.ContainsKey(id))
{
tenants.Add(id, options);
}
}
public T GetTenantContext<T>(string id) where T : DbContext
{
DbContextOptions options;
if (tenants.TryGetValue(id, out options))
{
return (T)Activator.CreateInstance(typeof(T), options);
}
return null;
}
}
ContextFactory , :
public static class ExtensionMethods
{
public static void AddMultiTenancy(this IServiceCollection services, IConfiguration config)
{
var tenantContextFactory = new TenantContextFactory();
var tenants = config.GetSection("Tenants");
var values = tenants.GetChildren();
foreach (var key in values)
{
foreach (var item in key.GetChildren())
{
var tenantId = item.Key.Split(':').Last();
var connectionString = item.Value;
var builder = new DbContextOptionsBuilder();
builder.UseSqlServer(connectionString);
tenantContextFactory.RegisterTenant(tenantId, builder.Options);
}
}
services.AddInstance(typeof(ITenantContextFactory), tenantContextFactory);
}
}
factory , , .
. :
EF7? ()
, :
System.InvalidOperationException: . OnConfiguring DbContext AddDbContext .
, DbContext AddDbContext DbContexts .
, , , , EF shell script. , , , .
:
, , , :
using (var context = tenantContextFactory.GetTenantContext<MyContext>(tenantId))
{
context.Database.Migrate();
}
, , , .
ASP.NET?
.
.
?
. , , ? ?
EDIT:
EF7. - ASP.NET.