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

news/2024/5/20 0:28:33 标签: .netcore, c#, ef core, 数据库

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

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

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

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

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

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

定义约束

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

public interface ISoftDelete
{
    public bool IsDeleted{get; set;}
}

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

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

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()

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接口来标记对应的实体。

public interface IReadOnly
{

}
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);
    }
}

http://www.niftyadmin.cn/n/5299252.html

相关文章

TMC2208-LA单轴步进驱动器可替代A4988

TMC2208是由德国TRINAMIC公司推出的步进电机驱动&#xff0c;TMC22XX系列步进电机驱动被广泛应用于各个行业。TMC2208为3D打印、相机、扫描仪和其他自动化设备应用提供集成电机驱动器解决方案。同TMC2208一样在3D打印行业运用较多的还有TMC2209/2130/2660/2225/等。另外还有TMC…

Vue3全局属性app.config.globalProperties

文章目录 一、概念二、实践2.1、定义2.2、使用 三、最后 一、概念 一个用于注册能够被应用内所有组件实例访问到的全局属性的对象。点击【前往】访问官网 二、实践 2.1、定义 在main.ts文件中设置app.config.globalPropertie import {createApp} from vue import ElementPl…

SSH 连接与RDP连接

SSH 连接 配置 Linux 上的 SSH 服务: 在 Linux 系统上&#xff0c;您需要确保安装并启动 SSH 服务器&#xff08;通常是 OpenSSH&#xff09;。您需要知道 Linux 机器的 IP 地址和 SSH 服务的端口号&#xff08;默认是 22&#xff09;。 在 Windows 上使用 SSH 客户端: 您可以…

Node.js+Express 路由配置,实现接口分类管理

首先创建一个路由目录及文件 routes/user.js代码 const express require(express); const router express.Router(); // 使用express提供的router对象 const db require(../dbserver/mysql);router.get(/api/user, (req, res) > {const sqlStr SELECT * FROM sys_user;…

uniapp开发日期预约表的方法和优化

如图所示&#xff0c;最近在开发预约时间表 但是设计稿看起来排列比较灭有规律&#xff0c;而uniapp的插件图表其实并没有类似的&#xff0c;因此只能自己想办法了&#xff0c; 直接就按前端实现的路子&#xff0c;仿造了一个时间表

【仅供测试】

https://microsoftedge.microsoft.com/addons/detail/%E7%AF%A1%E6%94%B9%E7%8C%B4/iikmkjmpaadaobahmlepeloendndfphd 测试网站&#xff1a; https://www.alipan.com/s/tJ5uzFvp2aF // UserScript // name 阿里云盘助手 // namespace http://tampermonkey.net/ // …

用PHP搭建一个绘画API

【腾讯云AI绘画】用PHP搭建一个绘画API 大家好&#xff01;今天我要给大家推荐的是如何用PHP搭建一个绘画API&#xff0c;让你的网站或应用瞬间拥有强大的绘画能力&#xff01;无论你是想要让用户在网页上绘制自己的创意&#xff0c;还是想要实现自动绘画生成特效&#xff0c;这…

WINDOWS 批量修改图片文件名称(流星程序集之二十)

博主家里有一台电脑&#xff0c;存放家庭全部的照片和视频&#xff0c;从智能手机和3G网络发展开始&#xff0c;家里的照片和视频越来越多&#xff0c;已经达到上万个文件。终于&#xff0c;博主找到一个方法整理和保存这些珍贵的数据资料。 一、按年代目录整理照片和视频 按年…