Добавление глобальных фильтров вне OnModelCreating - PullRequest
0 голосов
/ 16 апреля 2020

У меня есть мультитенантное приложение, в котором пользователь после успешного входа перенаправляется на страницу, где он может выбрать предприятие, с которым он хочет работать.

Мне нужно добавить множество глобальных фильтров, которые зависят от Идентификатор предприятия Но, похоже, OnModelCreating вызывается при создании dbcontext. И dbcontext создается, когда пользователь хочет войти в систему, но в настоящее время я еще не знаю предприятие, с которым он хочет работать, потому что он еще не выбрал его. Итак, я не могу добавить фильтры.

Я хотел бы добавить эти фильтры, когда выбран корпоративный идентификатор. Является ли это возможным? Потому что все примеры, которые я видел, добавляют их в 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);
    }
}

Как бы ты делаешь это?

1 Ответ

0 голосов
/ 19 апреля 2020

Наконец я понял. Я использую 2 DbContexts, как предложил Джеффри Паркс, и класс IModelCacheKeyFactory для генерации разных ключей для каждого арендатора.

DBContext, который должен запускаться OnModelCreating в зависимости от арендатора, должен быть определен как:

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, который был ранее вставлен и проверен промежуточным программным обеспечением. Вы можете заменить его своим кодом, чтобы получить арендатора. Это может быть служба и т. Д. c.

Генератор ключей для кэширования моделей довольно прост:

public class TenantModelCacheKeyFactory : IModelCacheKeyFactory
{
    public object Create(DbContext context) =>

        context is ApplicationDbContext dynamicContext
            ? (context.GetType(), dynamicContext.EnterpriseId)
            : (object)context.GetType();

}

Обратите внимание, как он извлекает tenantId из созданного DbContext (то есть создается для каждого HTTP-запроса).

После этого, если вы запустите запрос с правильным идентификатором клиента, после вызова OnModelCreating для этого клиента будет создан новый кэш модели, и все будет работать гладко. Если другой пользователь входит в систему для того же арендатора, новая модель не будет создана и кэшированная будет обслуживаться (помните, что DbContext всегда создается для запроса, поскольку он был добавлен в качестве службы с областью действия). Если другой пользователь входит в систему с другим tenantId, снова будет вызываться OnModelCreating, и будет также создан и обслужен другой кэш модели.

...