我有一个多租户应用程序,在该应用程序中,用户成功登录后将被重定向到一个页面,在该页面上,他可以选择要与之合作的企业。
我需要添加许多依赖于企业ID的全局过滤器。但是,似乎在创建dbcontext时调用了OnModelCreating。 dbcontext是在用户想要登录时创建的,但是,由于我尚未选择要与之共事的企业,因此我目前还不知道他想与之合作的企业。因此,我无法添加过滤器。
我想在选择企业ID时添加这些过滤器。可能吗?因为我看到的所有示例都在OnModelCreating中添加了它们,但在其他地方却找不到任何参考。
这是OnModelCreating方法的示例:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//Last in wins so first set base model features
base.OnModelCreating(modelBuilder);
HttpContext httpContext = _httpContextAccessor.HttpContext;
if (httpContext.Request.Headers.ContainsKey("X-TENANT-ID") == true)
{
string tenant = httpContext.Request.Headers["X-TENANT-ID"];
int enterpriseId = int.Parse(tenant);
//Define global filters for all entities
modelBuilder.Entity<Enterprise>().HasQueryFilter(p => !p.IsDeleted);
modelBuilder.Entity<Vehicle>().HasQueryFilter(o => o.EnterpriseId == enterpriseId && !o.IsDeleted);
modelBuilder.Entity<BillingInfo>().HasQueryFilter( o => o.EnterpriseId == enterpriseId && !o.IsDeleted);
modelBuilder.Entity<DriverAppUser>().HasQueryFilter( o => o.EnterpriseId == enterpriseId && !o.IsDeleted);
modelBuilder.Entity<Agency>().HasQueryFilter( o => o.EnterpriseId == enterpriseId && !o.IsDeleted);
modelBuilder.Entity<InsuranceCarrier>().HasQueryFilter( o => o.EnterpriseId == enterpriseId && !o.IsDeleted);
modelBuilder.Entity<Particular>().HasQueryFilter( o => o.EnterpriseId == enterpriseId && !o.IsDeleted);
}
}
您将如何做?
答案 0 :(得分:0)
最后我明白了。我正在使用Jeffrey Parks建议的2个DbContext和一个IModelCacheKeyFactory类来为每个租户生成一个不同的密钥。
根据租户需要运行OnModelCreating的DBContext需要定义为:
public class ApplicationDbContext : DbContext
{
private readonly IHttpContextAccessor _httpContextAccessor;
public int EnterpriseId { get; protected set; } = -1;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor httpContextAccessor) : base(options)
{
_httpContextAccessor = httpContextAccessor;
EnterpriseId = _httpContextAccessor.HttpContext.GetCurrentEnterpriseIdNotNull();
}
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//Last in wins so first set base model features
base.OnModelCreating(modelBuilder);
HttpContext httpContext = _httpContextAccessor.HttpContext;
int enterpriseId = httpContext.GetCurrentEnterpriseIdNotNull();
//Define global filters for all entities
modelBuilder.Entity<Vehicle>().HasQueryFilter(o => o.EnterpriseId == EnterpriseId && !o.IsDeleted);
...
}
}
请注意我如何使用GetCurrentEnterpriseIdNotNull方法获取TenantId。这是一个HTTPContext扩展方法,该方法从HttpContext.User获得声明,该声明先前已由中间件插入并验证。您可以将其替换为代码以获取租户。它可以是服务,等等。
用于缓存模型的密钥生成器非常简单:
public class TenantModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create(DbContext context) =>
context is ApplicationDbContext dynamicContext
? (context.GetType(), dynamicContext.EnterpriseId)
: (object)context.GetType();
}
注意如何从创建的DbContext(为每个HTTP请求创建的)中提取tenantId。
此后,如果您使用正确的租户ID运行请求,则为此租户调用OnModelCreating后将创建一个新的模型缓存,并且一切将顺利进行。如果另一个用户登录同一租户,则不会创建新模型,也不会提供缓存的模型(请记住,始终为每个请求创建DbContext,因为它是作为范围服务添加的)。如果另一个用户使用不同的tenantId登录,则会再次调用OnModelCreating,并且还将创建另一个模型缓存并提供服务。