如何使用“Entity Framework Core”(又名 EF7)实现“软删除”?

2023-12-21

我正在尝试使用 EF7 实现“软删除”。我的Item表有一个名为IsDeleted类型的bit。我在 SO 和其他地方看到的所有例子都使用这样的东西:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Item>().Map(m => m.Requires("IsDeleted").HasValue(false));
}

but Map()不再是一种方法ModelBuilder.

编辑:让我澄清一下。我现在主要只对读取数据感兴趣。我希望 EF 自动过滤掉我的所有记录Item表在哪里IsDeleted == 1(或真实)。我不想要求&& x.IsDeleted == false在每个查询的末尾。


现在是 2021 年,我想到添加一个与当前版本的 EF Core 相关的更现代、标准、内置的解决方案。

With 全局查询过滤器 https://learn.microsoft.com/en-us/ef/core/querying/filters您可以确保某些过滤器始终应用于某些实体。您可以通过接口定义软删除属性,这有助于以编程方式将过滤器添加到所有相关实体。看:


...

public interface ISoftDeletable
{
    public string DeletedBy { get; }
    public DateTime? DeletedAt { get; }
}

...

// Call it from DbContext.OnModelCreating()
private static void ConfigureSoftDeleteFilter(ModelBuilder builder)
{
    foreach (var softDeletableTypeBuilder in builder.Model.GetEntityTypes()
        .Where(x => typeof(ISoftDeletable).IsAssignableFrom(x.ClrType)))
    {
        var parameter = Expression.Parameter(softDeletableTypeBuilder.ClrType, "p");

        softDeletableTypeBuilder.SetQueryFilter(
            Expression.Lambda(
                Expression.Equal(
                    Expression.Property(parameter, nameof(ISoftDeletable.DeletedAt)),
                    Expression.Constant(null)),
                parameter)
        );
    }
}

然后,为了确保在删除期间使用此标志而不是硬删除(替代例如存储库设置标志而不是删除实体):

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
    foreach (var entry in ChangeTracker.Entries<ISoftDeletable>())
    {
        switch (entry.State)
        {
            case EntityState.Deleted:
                // Override removal. Unchanged is better than Modified, because the latter flags ALL properties for update.
                // With Unchanged, the change tracker will pick up on the freshly changed properties and save them.
                entry.State = EntityState.Unchanged;
                entry.Property(nameof(ISoftDeletable.DeletedBy)).CurrentValue = _currentUser.UserId;
                entry.Property(nameof(ISoftDeletable.DeletedAt)).CurrentValue = _dateTime.Now;
                break;
        }
    }
    return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}

注意事项 1:级联删除时机

一个关键的方面是考虑相关实体的级联删除,要么禁用级联删除,要么了解和控制 EF Core 的级联删除时序行为。默认值CascadeDeleteTiming设置是CascadeTiming.Immediate,这会导致 EF Core 立即将“已删除”实体的所有导航属性标记为EntityState.Deleted,并恢复EntityState.Deleted仅在根实体上的状态不会在导航属性上恢复它。因此,如果您的导航属性不使用软删除,并且您希望避免它们被删除,则也必须处理它们的更改跟踪器状态(而不是仅仅处理它,例如ISoftDeletable实体),或更改CascadeDeleteTiming设置如下图。

对于拥有的类型 https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities用于软删除实体。使用默认删除级联计时,EF Core 还将这些拥有的类型标记为“已删除”,如果它们设置为必需/不可为空,则在尝试保存软删除实体时将遇到 SQL 更新失败。

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
    ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
}

注意事项 2:对其他根实体的影响

如果您以这种方式定义全局查询过滤器,EF Core 将尽力隐藏引用软删除实体的所有其他实体。

例如,如果您软删除了Partner实体,并且你有Order其中每个实体都通过(必需)导航属性引用合作伙伴,然后,当您检索订单列表并包含合作伙伴时,所有引用软删除的订单Partner将从列表中消失。

此行为在文档页面底部 https://learn.microsoft.com/en-us/ef/core/querying/filters#accessing-entity-with-query-filter-using-required-navigation.

遗憾的是,从 EF Core 5 开始,全局查询过滤器不提供将其限制为根实体或仅禁用其中一个过滤器的选项。唯一可用的选项是使用IgnoreQueryFilters()方法,禁用所有过滤器。并且自从IgnoreQueryFilters()方法需要一个IQueryable并且还返回一个IQueryable,您不能使用此方法透明地禁用 DbContext 类中公开的过滤器DbSet.

不过,一个重要的细节是,只有当您Include()查询时给定的导航属性。有一个有趣的解决方案,用于获取结果集,该结果集将查询过滤器应用于某些实体,但没有将它们应用于其他实体,这依赖于 EF 的一个鲜为人知的功能,关系修复。基本上,您加载一个列表EntityA具有导航属性EntityB(不包括EntityB)。然后你单独加载列表EntityB, using IgnoreQueryFilters()。发生的情况是 EF 自动设置EntityB导航属性打开EntityA到加载的EntityB实例。这样查询过滤器就被应用到EntityA本身,但没有应用于EntityB导航属性,这样你就可以看到EntityA即使是软删除的EntityBs. 请参阅另一个问题的这个答案 https://stackoverflow.com/a/63280491/2906385。 (当然这会对性能产生影响,并且您仍然无法将其封装在 DbContext 中。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用“Entity Framework Core”(又名 EF7)实现“软删除”? 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 在模板类中声明模板友元类时出现编译器错误

    我一直在尝试实现我自己的链表类以用于教学目的 我在迭代器声明中指定了 List 类作为友元 但它似乎无法编译 这些是我使用过的 3 个类的接口 Node h define null Node
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 无限循环与无限递归。两者都是未定义的吗?

    无副作用的无限循环是未定义的行为 看here https coliru stacked crooked com view id 24e0a58778f67cd4举个例子参考参数 https en cppreference com w cpp
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 如何在 Linq to SQL 中使用distinct 和 group by

    我正在尝试将以下 sql 转换为 Linq 2 SQL select groupId count distinct userId from processroundissueinstance group by groupId 这是我的代码
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • 当文件流没有新数据时如何防止fgets阻塞

    我有一个popen 执行的函数tail f sometextfile 只要文件流中有数据显然我就可以通过fgets 现在 如果没有新数据来自尾部 fgets 挂起 我试过ferror and feof 无济于事 我怎样才能确定fgets 当
  • 从 mvc 控制器使用 Web api 控制器操作

    我有两个控制器 一个mvc控制器和一个api控制器 它们都在同一个项目中 HomeController Controller DataController ApiController 如果我想从 HomeController 中使用 Dat

随机推荐

  • mysql加载数据本地infile

    我正在尝试使用下面的代码使用 LOAD DATA LOCAL INFILE 将数据加载到 mysql 表中 Mysql LOAD DATA INFILE var www vhosts domain com httpdocs test1 cs
  • 有人可以帮我编译 PHP runkit DLL 扩展吗?

    我放弃 我问过这个问题 https stackoverflow com questions 4667333 where can i get runkit dll extensions for php 5 3前一段时间 但我又陷入了这个问题
  • JavaCard 中的类字节到底是什么?

    我已经开始使用 JavaCards 并试图掌握 CLA 字节的含义 如果要读RFC 5 4 1 类字节 http www cardwerk com smartcards smartcard standard ISO7816 4 5 basi
  • Android studio - 部署发布 apk 而不是调试

    Android Studio 中的运行配置仅允许您部署默认 调试 APK 但我已经通过从 Android Studio 中运行 gradle assembleDebug 作为外部工具 构建了一个发布 APK 并且希望改为部署它 但似乎您无法
  • GWT RequestFactory:如何从 stableId() 获取持久 id?

    I use Long我的实体中的 ids 不仅将它们存储在数据存储中 而且还引用其他实体 现在 我使用 RequestFactory 在客户端上 create 对象并保留它们 但我需要一种方法来找出服务器生成的 id 这是我发现的一种需要两
  • 在iOS系统上以编程方式从进程pid获取其他应用程序的目录路径?

    如何从进程pid中获取其他应用程序的目录路径 iOS 上似乎没有 proc pidpath 调用 以下适用于 iOS 并使用sysctl像 Activity Monitor Touch 这样的应用程序正在 App Store 中使用 因此
  • Nohup 与 Anaconda ipython 失败

    我正在尝试使用 Anaconda IPython 的 nohup 命令运行代码 如果我在 ipython 环境中运行我的代码 则它可以正常运行 几个小时 irsacf00 debian WISE AP gt ipython Python 3
  • 在elasticsearch中过滤_id范围

    我正在尝试按范围过滤 elasticsearch 中的 id 字段 索引未启用 是否可以 如果可以的话 可以怎样做呢 我在elasticsearch文档中读到我们可以使用 ids 通过 id和类型进行查询 但我不知道如何使用范围过滤器来完成
  • as3 播放影片剪辑一次

    如果您帮助我解决这个问题 我将非常感激 我正在尝试在 ipad 应用程序中播放一次 MovieClip 我试图以这种方式停止 但电影不会停止 var loader Loader new Loader var swfFile URLReque
  • PHP - 将 2d 数组转换为按特定值分组的 3d 数组的最快方法

    我想转换这个二维记录数组 records gt Array 0 gt Array 0 gt Pears 1 gt Green 2 gt Box 3 gt 20 1 gt Array 0 gt Pears 1 gt Yellow 2 gt P
  • 数据表列未对齐和重复排序箭头问题

    每次我显示表格时 我都会在表格中添加一个额外的排序箭头 th Here is an image of the issue I also am having an issue with my columns not aligning corr
  • Spring Security 和多部分请求

    我有一个受 Spring Security 和 OAuth2 保护的 Controller 我试图让我的用户上传文件 Controller RequestMapping value api image public class ImageC
  • OpenCV:选择颜色过滤的 HSV 阈值

    为了从图像中过滤出某种颜色 有必要设置需要检测的颜色的边界 我有一种感觉 这主要是一个试错过程 有没有什么方法可以快速找到特定颜色的正确阈值 在这种特定情况下 我试图检测下图中图表的灰色区域 当然 这没有检测到虚线 对于这个例子 我需要非常
  • 如何在 NSPasteBoard cocoa os x 中复制图像?

    I have tried this one but not working NSPasteboard pboard NSPasteboard generalPasteboard pboard declareTypes NSMutableAr
  • 网络设备发现

    对于我的 Android 应用程序 用户需要连接到托管在同一 LAN 上某处的服务器 同一 LAN 上可以托管多个服务器 为了方便用户 我打算扫描 Android 设备当前连接的 LAN 然后列出所有运行服务器的网络设备 而不是让用户将 I
  • 发布请求被切断

    我的 javascript 发布请求有问题 我有以下代码用于发送帖子请求 var xhttp new XMLHttpRequest xhttp onreadystatechange function if this readyState 4
  • Spark 和 Cassandra Java 应用程序:线程“主”java.lang.NoClassDefFoundError 中出现异常:org/apache/spark/sql/Dataset

    我得到了一个令人惊奇的 siplme java 应用程序 我几乎从这个例子中复制了它 我想做的就是读取表数据并显示在 Eclipse 控制台中 我的 pom xml
  • 减少 Android 应用程序 (apk) 的大小

    我现在将在 Google Play 商店上发布我的第一个应用程序 我已经压缩了我的应用程序中使用的图像 我对应用程序大小有一些疑问 如果应用程序的大小小于 2 MB 则用户卸载该应用程序的可能性较小 对于属于教育领域的应用程序 我的这种说法
  • 一个 Hive 脚本可以从另一个 Hive 脚本运行吗?

    我创建了两个配置单元脚本 script1 hql 和 script2 hql 是否可以从 script1 hql 运行脚本 script2 hql 我读到了有关使用 source 命令的信息 但无法回避它的使用 任何指针 参考文档将不胜感激
  • 如何使用“Entity Framework Core”(又名 EF7)实现“软删除”?

    我正在尝试使用 EF7 实现 软删除 我的Item表有一个名为IsDeleted类型的bit 我在 SO 和其他地方看到的所有例子都使用这样的东西 protected override void OnModelCreating ModelB