首页 > 解决方案 > 基于接口的全局过滤器

问题描述

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

//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 和 Invite 的接口,但 EF 抛出错误,说它必须是引用类型。

我还尝试了一个基类,但我不想更改似乎需要使其工作的基表。

还有其他选择吗?

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

解决方案


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

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

使用通用代码通过 fluent 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);

推荐阅读