基于接口的全局过滤器

Gre*_*Gum 3 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);
Run Code Online (Sandbox Code Playgroud)

我已经尝试过使用Item和Invite的Interface,但是EF抛出错误,说它必须是引用类型。

我也尝试了一个基类,但是我不想更改似乎需要进行该工作的基表。

还有其他选择吗?

Iva*_*oev 6

假设您的实体实现了以下接口:

public interface ITenantEntity
{
    int TenantId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

使用通用代码通过流利的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);
}
Run Code Online (Sandbox Code Playgroud)

然后,在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);
Run Code Online (Sandbox Code Playgroud)