有没有办法防止 Entity Framework 从不读取 属性 是特定值的条目?

Is there a way to prevent Entity Framework to NEVER read entries where a property is a specific value?

我有一个软件已经使用了一段时间,今天我们的客户决定我们不删除任何数据,而是隐藏它们。为此,我计划向所有表添加一个 "isDeleted" 属性 并更改所有删除方法以将此 属性 设置为 "true"。

问题是,我的阅读量是删除量的 1000 倍,我可以拥有一个用户并尝试使用实体关系读取该用户的所有评论,我必须为每个评论添加一个 "Where(x => !x.isDeleted)"像这样阅读,或者如果可能的话,选择不读取所有 isDeleted 为 true 的数据。

后者有可能吗?如果没有,是否可以替代写入 "Where(x => !x.isDeleted)" 一千次?

我过去曾研究过这个问题,推出自己的解决方案比您最初想象的要困难得多,主要是因为很难改变 Include 语句加载相关实体的方式(EF 确实不允许您过滤它们)。

但是有一个图书馆可以为您完成。

过滤读取结果

使用 EntityFramework.DynamicFilters 库可以很容易地完成。 (我与开发者没有任何关系,我真的很喜欢他们的图书馆)

主要自述文件实际上有一个适合您的用例的示例:

modelBuilder.Filter("IsDeleted", (ISoftDelete d) => d.IsDeleted, false);

基本上,它只会 return 结果 Where(d => !d.IsDeleted),这正是您想要的。此过滤器适用于所有直接提取 包含语句,这意味着就您的域而言,这些软删除实体基本上不存在。

这确实假定您的实体都派生自具有删除标志的共享根,无论如何我都建议您这样做。

软删除实体

也可以在您的数据库上下文中将硬删除转换为软删除,这意味着您不需要重写删除代码来更新实体(这可能是一个麻烦的重写,而且它总是可能有人忘记了它)。

您可以在您的上下文 class 中覆盖 SaveChanges(和 SaveChangesAsync)行为。这使您可以找到所有要删除的实体,并让您可以选择将其转换为更新语句,同时还提高 IsDeleted 标志。

也保证了没有人会忘记软删除。您的开发人员可以简单地硬删除实体(在处理代码时),上下文将为它们转换它。

public class MyContext : DbContext
{
    public override int SaveChanges()
    {
        ConvertHardDeleteToSoftDelete();

        return base.SaveChanges();
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        ConvertHardDeleteToSoftDelete();

        return await base.SaveChangesAsync(cancellationToken);
    }

    private void ConvertHardDeleteToSoftDelete()
    {
        var deletedEntries = ChangeTracker
                                   .Entries<ISoftDelete>()
                                   .Where(entry => entry.State == EntityState.Deleted)
                                   .ToList();

        foreach (var entry in deletedEntries)
        {
            entry.State = EntityState.Modified;
            entry.IsDeleted = true;
        }
    }
}

结合上面的动态过滤建议,这意味着这样的软删除实体不会再次出现在您的应用程序中,但它仍然存在于数据库中。