EF Core - 使用动态全局过滤器的多租户

问题描述 投票:0回答:1

我正在尝试使用单个数据库在应用程序中实现多租户。我引入了一个中间件,用于获取每个请求的 TenantId 和一个模型构建器的全局过滤器,用于按 TenantId 过滤所有相关实体。

我的问题是 TenantId =tenantAccessor.TenantId 的值不是根据 http 请求动态设置的,而是在模型创建期间应用一次默认 TenantID 值为 0 的过滤器。

    public static ModelBuilder AddTenantQueryFilter(this ModelBuilder modelBuilder, ITenantAccessor tenantAccessor)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            if (typeof(TenantBaseEntity).IsAssignableFrom(entityType.ClrType))
            {
                var method = typeof(TenantsExtension)
                    .GetMethod(nameof(GetTenantFilter), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
                    .MakeGenericMethod(entityType.ClrType);

                var filter = method.Invoke(null, new object[] { tenantAccessor });
                entityType.SetQueryFilter((LambdaExpression)filter);
            }
        }
        return modelBuilder;
    }

    private static LambdaExpression GetTenantFilter<TEntity>(ITenantAccessor tenantAccessor) where TEntity : TenantBaseEntity
    {
        Expression<Func<TEntity, bool>> filter = e => e.TenantId == tenantAccessor.TenantId;
        return filter;
    }

这是我当前的过滤器。我需要更改什么才能使我的过滤器应用/使用请求的当前 TenantId 值?
其余的实施工作。中间件被调用并为每个请求正确设置 TenantId,如果我在创建模型时手动将其设置为现有 TenantId,则过滤器也可以工作。

这是我尝试过的另一个过滤器实现,但有同样的问题:

    public static ModelBuilder AddTenantQueryFilter(this ModelBuilder modelBuilder, ITenantAccessor tenantAccessor)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            if (typeof(TenantBaseEntity).IsAssignableFrom(entityType.ClrType))
            {
                var parameter = Expression.Parameter(entityType.ClrType, "e");
                var property = Expression.Property(parameter, nameof(TenantBaseEntity.TenantId));
                var constant = Expression.Constant(tenantAccessor.TenantId);
                var body = Expression.Equal(property, constant);
                var lambda = Expression.Lambda(body, parameter);

                modelBuilder.Entity(entityType.ClrType).HasQueryFilter(lambda);
            }
        }
        return modelBuilder;
    }
entity-framework filter multi-tenant
1个回答
0
投票

您的解决方案有两个问题:

  1. 查询过滤器只能利用可直接从
    DbContext
    访问的属性。
  2. 每个
    OnModelCreating
    实例仅调用
    DbContext
    方法一次,从而使您的
    tenantAccessor
    参数无效。

让我们考虑以下

DbContext
后代的实现,它解决了这些问题:

public class MyDbContext : DbContext
{
    public ITenantAccessor? TenantAccessor { get; set; }

    public int? TenantId => TenantAccessor?.TenantId;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Entity configuration

        modelBuilder.ApplyQueryFilter<TenantBaseEntity>(e => e.TenantId == TenantId);
    }    
}

在此示例中,我使用了扩展方法 ApplyQueryFilter,它简化了此类过滤器的应用。

这里,

TenantId
DbContext
的属性,满足第一个要求。
TenantAccessor
属性可以通过依赖注入或中间件设置,因此
OnModelCreating
仅被调用一次这一事实并不重要。

© www.soinside.com 2019 - 2024. All rights reserved.