通过一次调用 SaveChanges() 进行多个 Dbset/实体修改

Posted

技术标签:

【中文标题】通过一次调用 SaveChanges() 进行多个 Dbset/实体修改【英文标题】:Multiple Dbset/entity modification with single call to SaveChanges() 【发布时间】:2021-09-19 01:35:48 【问题描述】:

我正在开发一个 .NET Core Web API,它需要使用 EF Core 5.0.2 与 Azure SQL 数据库进行交互。

我有不同的存储库方法,我与DbContext 交互以添加/编辑/删除不同DbSet 的记录。

例如:

UserRepository.AddUser(userdata);

AddUser的实现是这样的,

ourDbContext.UserTable.AddAsync(userdata);

所以在用户服务方法中,依次调用不同的存储库方法,这些方法都没有单独调用ourDbContext.SaveChangesAsync()。在所有存储库方法调用之后都会出现对SaveChanges 的单个调用,这就像一个工作单元模式,所有调用都作为单个事务处理。

例子:

UserRepository.AddUser(userdata);
ActivityRepository.AddActivity("New User got added");
ourDbContext.SaveChangesAsync();

所以我的问题是:如果对任何表/实体的任何保存更改失败,是否会回滚之前成功的表更改?

例如,假设这个操作

UserRepository.AddUser(userdata);

成功,新的用户记录已添加到用户表中。

但这并不成功:

ActivityRepository.AddActivity("New User got added");

因此,Activity 表中没有添加任何活动记录。

SaveChangesAsync() 是否能够自动处理这种情况并回滚用户表的新更改?

如果不是,我们是否应该用事务范围包装上述代码?或者推荐的方法是什么。

【问题讨论】:

A DbContext 是一个多实体 工作单元。像这样拥有多个“存储库”会打破这一点。此外,只有 CRUD 方法的类是Data Access Objects,而不是存储库。 DbContext 之上的真实存储库将在比单个实体更高 级别抽象操作。例如,在调用SaveChanges 之前检索客户的订单、添加新商品并更新客户的奖励积分以一次保存所有更改 【参考方案1】:

简述DbContext's Change Tracker 的工作原理:

您加载实体:ChangeTracker 会记住所有加载实体的当前值(除非您使用 AsNoTracking()) 您已修改加载的实体,删除,添加新的。 您调用SaveChanges:ChangeTracker 通过与之前的值进行比较,开始搜索自上次加载以来更改了哪些对象。 生成 DML SQL,并将所有内容保存在一个 SQL 语句或 Transaction 中的多个语句中。

所以,如果每个存储库都有一个DbContext,则无需担心回滚,只需不要调用SaveChanges()。为了确保重启过程,您必须重新创建DbContext,因为它包含不需要的状态。

【讨论】:

对每个“存储库”使用不同的 DbContext 会破坏这一点。只有当所有存储库都使用相同的 DbContext 实例时,UoW 功能才会起作用 @PanagiotisKanavos,我已经提到过,不是吗? 我已经检查了所有这些数据访问对象类中的 DbContext 实例是否等于用作 UoW SaveChanges() 的 DbContext 实例(即 ourDbContext.SaveChangesAsync())。所以调用所有这些单独的实体修改最后带有单个 SaveChanges() 的方法应该被视为单个事务对吗? @SvyatoslavDanyliv 你写了相反的if you have one DbContext for each repository, you do not need to worry about rollbacking, just do not call SaveChanges()。每个存储库使用一个 DbContext,您失去回滚所有更改的能力。如果您需要进行 5 次 SaveChanges 调用并且最后一次失败,则需要一个外部事务来回滚所有这些调用 @RiktimMondal 是的,但是您必须进行检查的事实是对您当前设计的非常非常强烈的警告反对。显然,这些“存储库”不能独立工作。对于这个特定的用例/场景/有界上下文,您需要一个结合所有这些的single 存储库。DbContext 不是数据库的模型。您可以并且应该拥有多个 DbContext,其中包含针对特定场景量身定制的实体

以上是关于通过一次调用 SaveChanges() 进行多个 Dbset/实体修改的主要内容,如果未能解决你的问题,请参考以下文章

使用 EFCore.BulkExtensions 时是不是需要调用 SaveChanges

添加一批实体。调用 SaveChanges() 时如何确定哪些实体失败

如果在 Db 上修改了先前加载的实体,如何将 EF 设置为不让 saveChanges?

EF6 - Update()失败,多个SaveChangeAsync,但没有多个SaveChanges()

记录一次BUG修复-Entity Framwork SaveChanges()失效

记录一次BUG修复-Entity Framwork SaveChanges()失效