如果每个租户都有自己的数据库,那么这里的问题是如何动态决定需要注入哪个连接字符串以及如何使连接字符串可用。
我找到了一篇关于“在 ASP.NET Core 上实现每个租户策略的数据库”的文章,这清楚地解释了一切。 https://gunnarpeipman.com/aspnet-core-database-per-tenant/
首先定义新的租户类
public class Tenant
{
public int Id { get; set; }
public string Name { get; set; }
public string Host { get; set; }
public string DatabaseConnectionString { get; set; }
}
我们需要一个单独的 MasterDb 来存储租户配置。这意味着它需要单独的 DbContext 来表示与 MasterDb 的会话。
public class MasterDbContext : DbContext
{
private DbSet<Tenant> Tenants { get; set; }
public MasterDbContext(DbContextOptions<MasterDbContext> options) : base(options){}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tenant>().HasKey(e => e.Id);
}
public Tenant GetTenant(IHttpContextAccessor accessor)
{
Tenant tenant = Tenants.FirstOrDefault(t => t.Host.ToLower() == accessor.HttpContext.Request.Host.Value.ToLower());
return tenant;
}
}
现在是时候引入 ITenantProvider 并编写一个使用 MasterDbContext 的实现了。该提供程序包含有关检测主机标头和获取当前租户的所有逻辑。
public interface ITenantProvider
{
Tenant GetTenant();
}
public class TenantProvider : ITenantProvider
{
private readonly Tenant _tenant;
public TenantProvider(IHttpContextAccessor accessor,
MasterDbContext context)
{
_tenant = context.GetTenant(accessor);
}
public Tenant GetTenant()
{
return _tenant;
}
}
动态配置 OrganizationDbContext。
public class OrganizationDbContext : DbContext
{
private readonly Tenant _tenant;
public DbSet<Branch> Branches { get; set; }
public DbSet<Employee> Employees { get; set; }
public OrganizationContext(DbContextOptions<OrganizationContext> options,ITenantProvider tenantProvider): base(options)
{
_tenant = tenantProvider.GetTenant();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql(_tenant.DatabaseConnectionString);
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Branch>().HasKey(e => e.Id);
modelBuilder.Entity<Employee>().HasKey(e => e.Id);
base.OnModelCreating(modelBuilder);
}
}