AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 306964
Accepted
Mikkel
Mikkel
Asked: 2022-02-04 05:49:46 +0800 CST2022-02-04 05:49:46 +0800 CST 2022-02-04 05:49:46 +0800 CST

使用硬删除后向数据库添加软删除

  • 772

介绍

我的应用程序从一个集中的来源收集数据,许多不同的用户可以在其中提交有关其组织和员工的数据。以前,当用户数据与事实来源不再相关时,我们只是硬删除用户数据,因为它曾经是可靠的。

但是对客户使用的某些软件进行更改,一切都会变得混乱。现在,他们每月在提交数据时会多次删除所有数据。这是错误的,并且由于设计糟糕。这意味着他们丢失了我们系统中用户的数据,并且必须重新输入其中的一部分。

他们使用的软件很顽固,不会改变行为。我们已经尝试教育用户如何使用它,但他们没有学习。所以现在最后一个选项是软删除一段时间内的数据。

看过网络上的多个 Stack Overflow 帖子和博客后,我真的不喜欢任何选项,IE。在需要软删除的表中添加一列。我开始寻找是因为这也是我的第一直觉,但我并不真正喜欢它及其含义。

我想知道你是否可以就不同的想法给我一些反馈。我没有维护软删除的经验,我不知道我的想法是否很糟糕。

图表和关系 显示一些关系的简单图表

有一个用户,他们的唯一标识符在多个组织中是相同的。每个用户隶属于一个组织,他们有一些用户信息,如姓名、头衔等。在我们的系统中,他们有一个状态行,因为无论他们选择连接哪个组织,在我们的应用程序中都是相同的。

因此,如果我按照传统方式添加用于软删除的列,我将不得不为每个包含用户数据的唯一表添加一个,因为它们与某个组织的从属关系可能会被删除,但作为用户,它们仍然存在我们的系统来自其他地方。

但是,为了解决所有这些额外的列,我的代码的实质内容似乎很麻烦,而且需要进行大量更改。

主意

在我看来,如果我添加一个包含以下内容的单独表格会更简单:

  • 唯一用户标识符
  • 唯一组织标识符
  • 软删除日期

然后每当我的应用程序请求数据时,api 都会检查新表;“这个人是不是被本组织软删除了?” 如果为真,它们只会阻止请求,直到它们在需要时被恢复,或者它们将一直被删除,直到它们在软删除发生后的 x 小时内被硬删除。

不必到处更改许多查询和逻辑。

附加信息

该 API 使用 EFCore 作为 ORM 来连接到数据库,以防它有助于解决有关其功能集的任何其他智能修复。我曾考虑过创建自定义保存更改逻辑,但除了再次向所有表中添加一列之外,我想不出一个好主意。

如果您需要更多信息,请告诉我。

更新

JD 告诉我行级安全性,这让我环顾四周。它似乎非常有用,它让我对我可以搜索的内容有了更多的了解。

所以我遇到了 EFCore 的全局查询过滤器,这似乎很有希望。它允许上下文对所有查询进行过滤,当您实际上需要忽略此全局过滤器时,您可以简单地逐个查询地执行此操作。

如果您需要为基于连接用户的全局过滤器使用某些东西,它允许依赖注入。我根据这些新信息创建了一个答案

事实证明,我真正想要的是停用该行,直到最终激活或硬删除而不是软删除。我不知道表达自己的正确方式。

database-design delete
  • 1 1 个回答
  • 227 Views

1 个回答

  • Voted
  1. Best Answer
    Mikkel
    2022-02-09T02:04:46+08:002022-02-09T02:04:46+08:00

    JD 在评论中提到了行级安全性,对于需要做我想做的事但只能使用 SQL 的人来说,这似乎很有希望。如果他们发布答案,我会为这个问题给予他们信任。

    尽管可以在 EFCore 中创建允许在部署数据库时执行自定义 SQL 的迁移,但感觉是被迫的。但仍然可能是一个有效的解决方案

    MS docs 自定义迁移功能

    MS docs 原始迁移 SQL

    JD 的 ROW 级安全链接

    根据我词汇表中的新术语,我从 EFCore 中找到了全局查询过滤器,然后我就去了。

    微软自己的文档

    尽管您确实需要小心设置它的方式。

    使用所需的导航来访问定义了全局查询过滤器的实体可能会导致意外结果。

    但从本质上讲,它归结为在代码优先配置中添加这样的内容。

    modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog).IsRequired();
    modelBuilder.Entity<Blog>().HasQueryFilter(b => b.Url.Contains("fish"));
    modelBuilder.Entity<Post>().HasQueryFilter(p => p.Blog.Url.Contains("fish"));
    

    博客讨论一个真实世界的例子,他们讨论将用户 JWT 令牌信息注入上下文以允许对个人进行个人过滤。

    public class Context : DbContext
    {
        private readonly IClaimsProvider _claimsProvider;
    
        private int UserId => _claimsProvider.UserId;
        private IEnumerable<int> AccessibleClientIds => _claimsProvider.AccessibleClientIds;
    
        public Context(DbContextOptions<Context> options, IClaimsProvider claimsProvider) : base(options)
        {
            _claimsProvider = claimsProvider;
        }
        ...
    }
    
    
    modelBuilder.Entity<Client>(entity =>
        {
            entity.HasQueryFilter(x => AccessibleClientIds.Contains(x.Id));
    
            entity.HasKey(x => x.Id);
            entity.HasMany(x => x.UserClientAccess)
                  .WithOne(x => x.Client)
                  .HasForeignKey(x => x.ClientId);
        });
    
            modelBuilder.Entity<UserOptions>(entity =>
         {
             entity.HasQueryFilter(x => x.UserId == UserId);
    
             entity.HasKey(x => x.Id);
         });
    

    该博客讨论了将默认的 savechanges 功能覆盖为始终软删除,但这不是我想要的,但有些人可能会觉得它很有帮助。

        public override int SaveChanges()
        {
            UpdateSoftDeleteStatuses();
            return base.SaveChanges();
        }
    
        public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
        {
                UpdateSoftDeleteStatuses();
                return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
        }
    
        private void UpdateSoftDeleteStatuses()
        {
            foreach (var entry in ChangeTracker.Entries())
            {
                switch (entry.State)
                {
                    case EntityState.Added:
                        entry.CurrentValues["isDeleted"] = false;
                        break;
                    case EntityState.Deleted:
                        entry.State = EntityState.Modified;
                        entry.CurrentValues["isDeleted"] = true;
                        break;
                }
            }
        }
    

    最终实施

    我有所有相关模型继承自Deactivateable将属性/列添加到数据库。然后由于过滤器的简单性,我只是在上下文中创建了这个配置:

    foreach (var entity in modelBuilder.Model.GetEntityTypes())
            {
                if (entity.ClrType.IsSubclassOf(typeof(Deactivatable)))
                {
                    var dateTimeOffsetDefaultValue = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0));
                    modelBuilder.Entity(entity.ClrType, o =>
                    {
                        o.Property(nameof(Deactivatable.DeactivatedDate)).HasDefaultValue(dateTimeOffsetDefaultValue);
                    });
    
                    var deactivatedDate = entity.FindProperty(nameof(Deactivatable.DeactivatedDate)).PropertyInfo;
    
                    var parameterExpression = Expression.Parameter(entity.ClrType);
                    var propertyExpression = Expression.Property(parameterExpression, deactivatedDate);
                    var constExpression = Expression.Constant(dateTimeOffsetDefaultValue);
    
                    var equalExpression = Expression.Equal(propertyExpression, constExpression);
                    var filter = Expression.Lambda(equalExpression, parameterExpression);
                    modelBuilder.Entity(entity.ClrType).HasQueryFilter(filter );
                }
    
            }
    

    基于stevendarby发表的这个github 问题评论的灵感。我不知道它们是从哪里来的,所以我只是选择自己迭代模型并查看子类,基于我在相关 SO 帖子中看到的一个想法,我再也找不到和参考了。.Model.FindLeastDerivedEntityTypes

    • 0

相关问题

  • 过滤索引是否有助于改进基于输入时间的查询,还是应该避免这种情况?

  • MySQL VARCHAR 和 TEXT 数据类型有什么区别?

  • 存储计算值或根据要求重新计算它们更好吗?[复制]

  • 存储与计算聚合值

  • 在数据仓库中实现多对多关系有哪些方法?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve