Ef Core花里胡哨系列(1) SafeDelete、ReadOnly、Audit 安全删除、只读、审计等

Ef Core花里胡哨系列(1) SafeDelete、ReadOnly、Audit 安全删除、只读、审计等

在软件设计中,软删除是一种常见的数据管理技术,用于标记和隐藏被删除的数据,而不是永久地从数据库中删除它们。软删除通常通过在数据表中添加一个额外的标志列(例如"IsDeleted")来实现。当数据被删除时,该标志列被设置为指示删除状态的值(通常是true或1),而不是直接从数据库中删除数据记录。

使用软删除的主要原因是保留数据的完整性和可追溯性。通过软删除,我们可以避免永久删除数据,从而避免意外或不可逆的数据丢失。软删除还可以帮助我们满足法律、合规性或审计要求,因为我们可以跟踪和记录数据的删除历史。

另一个重要的原因是软删除可以提供更好的用户体验。软删除允许用户恢复被删除的数据,而不必联系管理员或支持团队。这对于误删除或需要恢复数据的情况非常有用。

然而,软删除也有一些潜在的缺点。首先,软删除会增加数据库的存储需求,因为被删除的数据仍然存在于数据库中。其次,软删除可能会导致查询和性能方面的复杂性,因为我们需要在查询中过滤掉已删除的数据。

总之,软删除是一种在软件设计中常见的数据管理技术,它通过标记和隐藏被删除的数据来保留数据的完整性和可追溯性。它提供了更好的用户体验,并满足法律和合规性要求。然而,软删除也有一些潜在的缺点,需要在设计和实现时加以考虑。

定义约束

我们先定义一个安全删除的接口,用于约束对应的实体类。

csharp 复制代码
public interface ISoftDelete
{
    public bool IsDeleted{get; set;}
}

通过重写ef core来实现对实现了该接口的成员进行自动处理

通过读取ef core上下文中追踪的实体,如果是继承自ISoftDelete接口,说明便不是直接删除数据,而是软删除即更新数据,将实体对应的IsDeleted标记更改为true,随后将状态改为EntityState.Modified即进行更新操作。

csharp 复制代码
public class SampleDbContext(DbContextOptions<SampleDbContext> options, IServiceProvider serviceProvider)
    : DbContext(options)
{

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
    {
        foreach (var entityEntry in ChangeTracker.Entries<IEntity>())
        {
            if (entityEntry is { Entity: ISafeDelete safeDelete, State: EntityState.Deleted })
            {
                safeDelete.IsDeleted = true;
                entityEntry.State = EntityState.Modified;
            }
        }

        return await base.SaveChangesAsync(cancellationToken);
    }

}

如何查询时自动过滤?

我们通过重写OnModelCreating方法,来预置一些Ef Core的行为,例如HasQueryFilter来预置一个过滤条件,如果是继承自ISoftDelete的实体,那便需要过滤掉已经软删除的数据。

HasQueryFilter仅可以配置一种过滤,且每次查询都会生效。如果有权限相关的管理,建议在仓储层通过权限来实现过滤,更灵活一些或者可以通过在lambda中使用IgnoreQueryFilters()来忽略过滤,例如:DbSet<TEntity>().IgnoreQueryFilters()

csharp 复制代码
public class SampleDbContext(DbContextOptions<SampleDbContext> options, IServiceProvider serviceProvider)
    : DbContext(options)
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            if (typeof(ISafeDelete).IsAssignableFrom(entityType.ClrType))
            {
                modelBuilder.Entity(entityType.ClrType).HasQueryFilter(GetFilterExpression(entityType.ClrType));
            }
        }

        base.OnModelCreating(modelBuilder);
    }

    private Expression<Func<IEntity, bool>> GetFilterExpression(Type type)
    {
        var parameter = Expression.Parameter(type, "e");
        var property = Expression.Property(parameter, nameof(ISafeDelete.IsDeleted));
        var body = Expression.Equal(property, Expression.Constant(false));
        return Expression.Lambda<Func<IEntity, bool>>(body, parameter);
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
    {
        foreach (var entityEntry in ChangeTracker.Entries<IEntity>())
        {
            if (entityEntry is { Entity: ISafeDelete safeDelete, State: EntityState.Deleted })
            {
                safeDelete.IsDeleted = true;
                entityEntry.State = EntityState.Modified;
            }
        }

        return await base.SaveChangesAsync(cancellationToken);
    }
}

联想扩展

我们通过ISoftDelete约束实现了软删除,那么我们可以实现其它什么操作呢?

我们可以实现一些基于逻辑的业务操作,例如只读,或者是审计信息等等。

只读ReadOnly

在软件设计中,我们一些表可能是记录型数据,是不允许更改的,不能简单的从接口上约束操作,我们可以实现一个IReadOnly接口来标记对应的实体。

csharp 复制代码
public interface IReadOnly
{

}
csharp 复制代码
public class SampleDbContext(DbContextOptions<SampleDbContext> options, IServiceProvider serviceProvider)
    : DbContext(options)
{
    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
    {
        foreach (var entityEntry in ChangeTracker.Entries<IEntity>())
        {
            // 模式匹配语法,即实体如果继承自 IReadOnly 接口且上下文的状态不是添加或者是无操作,即抛出异常,不允许该操作
            if (entityEntry is { Entity: IReadOnly, State: not (EntityState.Added and EntityState.Unchanged) })
            {
                throw new NotSupportedException();
            }
        }

        return await base.SaveChangesAsync(cancellationToken);
    }
}
相关推荐
南城花随雪。9 分钟前
硬盘(HDD)与固态硬盘(SSD)详细解读
数据库
儿时可乖了11 分钟前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
懒是一种态度12 分钟前
Golang 调用 mongodb 的函数
数据库·mongodb·golang
天海华兮15 分钟前
mysql 去重 补全 取出重复 变量 函数 和存储过程
数据库·mysql
神仙别闹41 分钟前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
gma9991 小时前
Etcd 框架
数据库·etcd
爱吃青椒不爱吃西红柿‍️1 小时前
华为ASP与CSP是什么?
服务器·前端·数据库
Yz98762 小时前
hive的存储格式
大数据·数据库·数据仓库·hive·hadoop·数据库开发
苏-言2 小时前
Spring IOC实战指南:从零到一的构建过程
java·数据库·spring
Ljw...2 小时前
索引(MySQL)
数据库·mysql·索引