我最近发现了全局过滤器,这很棒,因为我的任务是在我的应用程序中实现软删除。 目前我已经这样做了:
// Query filters https://docs.microsoft.com/en-us/ef/core/querying/filters
modelBuilder.Entity<Address>().HasQueryFilter(m => !m.Deleted);
modelBuilder.Entity<Attribute>().HasQueryFilter(m => !m.Deleted);
modelBuilder.Entity<Brand>().HasQueryFilter(m => !m.Deleted);
modelBuilder.Entity<BrandAddress>().HasQueryFilter(m => !m.Deleted);
modelBuilder.Entity<BrandCategory>().HasQueryFilter(m => !m.Deleted);
modelBuilder.Entity<Category>().HasQueryFilter(m => !m.Deleted);
// many more entity types....
所有实体都继承一个 BaseModel
,如下所示:
public class BaseModel
{
public Guid CreatedBy { get; set; }
public Guid UpdatedBy { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateUpdated { get; set; }
public bool Deleted { get; set; }
}
是否可以为继承 BaseModel
的任何类添加查询过滤器?
类似的东西:
modelBuilder.Entity<BaseModel>().HasQueryFilter(m => !m.Deleted);
所以我不会忘记(稍后)为我添加的模型添加查询过滤器?
答案 0 :(得分:5)
对于最新的 EF Core 版本(也应适用于 3.0,对于较早版本的表达式替换应手动处理,请参阅 ReplacingExpressionVisitor
调用)您可以使用一些 reflection(最少的)、expression trees 和 IMutableModel.GetEntityTypes
在您的 OnModelCreating
方法中。这样的事情应该可以工作:
// define your filter expression tree
Expression<Func<BaseModel, bool>> filterExpr = bm => !bm.Deleted;
foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes())
{
// check if current entity type is child of BaseModel
if (mutableEntityType.ClrType.IsAssignableTo(typeof(BaseModel)))
{
// modify expression to handle correct child type
var parameter = Expression.Parameter(mutableEntityType.ClrType);
var body = ReplacingExpressionVisitor.Replace(filterExpr.Parameters.First(), parameter, filterExpr.Body);
var lambdaExpression = Expression.Lambda(body, parameter);
// set filter
mutableEntityType.SetQueryFilter(lambdaExpression);
}
}
答案 1 :(得分:0)
您需要在运行时构造一个 lambda 表达式:
instance => !instance.IsDeleted
现在,假设我们有这个接口,以及一些实现这个接口(直接或传递)的实体:
interface ISoftDelete
{
bool IsDeleted { get; set; }
}
class Product : ISoftDelete
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
// ...
}
我们希望对实现此接口的所有实体应用软删除查询过滤器。
要查找在 DbContext 模型中注册的所有实体,我们可以使用 IMutableModel.GetEntityTypes()
。然后我们过滤所有实现 ISoftDelete
的实体并添加设置自定义查询过滤器。
这里有一个可以直接使用的扩展方法:
internal static class SoftDeleteModelBuilderExtensions
{
public static ModelBuilder ApplySoftDeleteQueryFilter(this ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (!typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
{
continue;
}
var param = Expression.Parameter(entityType.ClrType, "entity");
var prop = Expression.PropertyOrField(param, nameof(ISoftDelete.IsDeleted));
var entityNotDeleted = Expression.Lambda(Expression.Equal(prop, Expression.Constant(false)), param);
entityType.SetQueryFilter(entityNotDeleted);
}
return modelBuilder;
}
}
现在,我们可以在我们的 DbContext 中使用它:
class AppDbContext : DbContext
{
// ...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>();
modelBuilder.ApplySoftDeleteQueryFilter(); // <-- must come after all entity definitions
}
}