I'm developing a multi-tenant based application that will have a separate database for each school/tenant. Each schema will be identical to each other. The idea is to have one database context using .NET Core 3 and EF Core.
For example, the client navigates to school1.gov.uk, then SchoolContext is instantiated using the connection strings stored in appsettings.json under 'school1'.
Currently, I'm having to run add-migration against every new context added. Can anyone come up with a solution to run migrations once, under a single context i.e. SchoolContext ?
Main Context
public class SchoolContext : DbContext
{ protected readonly IConfiguration _configuration;
private readonly IHttpContextAccessor _httpContextAccessor;
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
public SchoolContext(IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
{
_configuration = configuration;
_httpContextAccessor = httpContextAccessor;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var subdomain = _httpContextAccessor.HttpContext.Request.Host.Host;
var connectionString = _configuration.GetConnectionString(subdomain);
optionsBuilder.UseSqlServer(connectionString);
}
}
Tenant Context 1
public class School1Context : SchoolContext
{
public Schoo1Context()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = _configuration.GetConnectionString("School1");
optionsBuilder.UseSqlServer(connectionString);
}
}
Tenant Context 2
public class School2Context : SchoolContext
{
public School2Context()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = _configuration.GetConnectionString("School2");
optionsBuilder.UseSqlServer(connectionString);
}
}
Program.cs
using (var scope = host.Services.CreateScope())
{
var school1Context = scope.ServiceProvider.GetService<School1Context>();
school1Context.Database.Migrate();
var school2Context = scope.ServiceProvider.GetService<School2Context>();
school2Context.Database.Migrate();
}
Appsettings.json
"ConnectionStrings": {
"School1": "Persist Security Info=true;Data Source=.\\SQLEXPRESS;Initial Catalog=School1;User ID=;Password=;",
"School2": "Persist Security Info=true;Data Source=.\\SQLEXPRESS;Initial Catalog=School2;User ID=;Password=;",
}
The biggest problem I see with your code is that you should have 1 context (because they're all the same schema, right?). If they had different schemas, you would need different contexts.
That single context should be instantiated with the school's specific connection string.
This way, you add-migration against 1 context (not multiple).
You're mostly there, just delete School1Context and School2Context, because here:
SchoolContext.cs
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var subdomain = _httpContextAccessor.HttpContext.Request.Host.Host;
var connectionString = _configuration.GetConnectionString(subdomain); // <--- this is tenanted already
optionsBuilder.UseSqlServer(connectionString);
}
You already grab the connection string and connect to a different database.
You stated the answer in your original question:
The idea is to have one database context using .NET Core 3 and EF Core.
Slight plug to a framework I love and use daily: ServiceStack has multitenancy built in - ServiceStack Multitenancy