基于接口的全局过滤器

时间:2018-05-14 12:28:25

标签: c# entity-framework entity-framework-core

是否可以在一行中写下以下内容?

//Create a Global Filter for the TenantId property.
modelBuilder.Entity<Item>().HasQueryFilter(b => EF.Property<int>(b, "TenantId") == this._appUserProvider.CurrentTenantId);
modelBuilder.Entity<Invite>().HasQueryFilter(b => EF.Property<int>(b, "TenantId") == this._appUserProvider.CurrentTenantId);

我尝试了一个Item for Item和Invite,但是EF抛出了一个错误,说它必须是一个引用类型。

我也尝试了一个基类,但我不想改变基本表,这似乎是完成这项工作所必需的。

还有其他选择吗?

1 个答案:

答案 0 :(得分:6)

假设您的实体实现以下界面:

public interface ITenantEntity
{
    int TenantId { get; set; }
}

使用公共代码通过流畅的API配置多个实体时最重要的是在调用modelBuilder.Entity<TEntity>()时使用真实实体类型。使用基类将引入EF继承,接口将简单地生成异常。

最简单的解决方案IMO将公共代码放在泛型方法中并通过反射调用它。

首先在派生的DbContext类中创建一个包含所需流畅配置的约束通用实例方法:

void ConfigureTenantFilter<TEntity>(ModelBuilder modelBuilder)
    where TEntity : class, ITenantEntity
{
    modelBuilder.Entity<TEntity>()
        .HasQueryFilter(e => e.TenantId == this._appUserProvider.CurrentTenantId);
}

然后在OnModelCreating覆盖结束时使用以下代码段(在发现所有实体类型之后):

var configureTenantMethod = GetType().GetTypeInfo().DeclaredMethods.Single(m => m.Name == nameof(ConfigureTenantFilter));
var args = new object[] { modelBuilder };
var tenantEntityTypes = modelBuilder.Model.GetEntityTypes()
    .Where(t => typeof(ITenantEntity).IsAssignableFrom(t.ClrType));
foreach (var entityType in tenantEntityTypes)
    configureTenantMethod.MakeGenericMethod(entityType.ClrType).Invoke(this, args);