对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

使用批处理进行事务更改

在本指南中,您可以学习如何在 EF Core 提供商应用程序中使用 事务。事务封装一系列写入操作。如果事务中的任何操作失败,提供商将回滚事务中的所有更改。此行为有助于确保数据一致性。

EF Core 提供商支持以下事务模式:

  • 隐式事务:提供商会自动将对 SaveChanges()SaveChangesAsync() 方法的调用包装在事务中。请参阅配置隐式事务,了解如何配置或禁用此行为。

  • 显式事务:您调用 Database.BeginTransaction() 方法手动开始事务,然后通过调用 Commit()Rollback() 提交或回滚事务。

本指南中的示例使用 sample_guides数据库中的 planets集合。此集合中的文档使用以下 Planet 类作为模型:

public class Planet
{
public ObjectId _id { get; set; }
public string name { get; set; } = null!;
public int orderFromSun { get; set; }
public bool hasRings { get; set; }
}

此集合来自Atlas提供的示例数据集。请参阅快速入门指南,学习;了解如何创建免费的MongoDB 集群并加载此示例数据。

在使用事务之前,请注意以下要求和限制:

  • 事务需要支持多文档事务的 MongoDB Server 部署,例如副本集或分片集群。如果应用程序尝试在独立运行的实例上启动事务,则提供商会抛出 NotSupportedException。为防止这种情况,请将 AutoTransactionBehavior.Never 设置为如 配置隐式事务 中所述。

  • EF Core 提供程序不支持 .NET 环境事务,例如由 System.Transactions.TransactionScope 创建的事务。如果您尝试使用环境事务,则提供程序会抛出异常。请使用隐式或显式事务。

默认情况下,当多个根实体受影响时,EF Core 提供商会自动将 SaveChanges()SaveChangesAsync() 调用包装在事务中。根实体是集合中的顶级文档,与存储在另一个文档中的所有或嵌入类型相反。

以下示例在单个 SaveChanges() 调用中插入两个行星。由于此操作影响多个根实体,因此提供商会自动将其包装在事务中。如果任何插入失败,提供商将回滚这两个更改。

选择 SynchronousAsynchronous 标签页,查看相应的代码。

dbContext.Planets.AddRange(
new Planet { Name = "Mercury" },
new Planet { Name = "Venus" }
);
// Both inserts succeed or both are rolled back
dbContext.SaveChanges();
dbContext.Planets.AddRange(
new Planet { Name = "Mercury" },
new Planet { Name = "Venus" }
);
// Both inserts succeed or both are rolled back
await dbContext.SaveChangesAsync();

DbContext 对象上的 Database.AutoTransactionBehavior 属性控制提供商何时使用隐式事务。此属性接受来自 AutoTransactionBehavior 枚举的值,该枚举包含以下值:

说明

WhenNeeded

仅当 SaveChanges()SaveChangesAsync() 影响多个根实体时,提供商才使用事务。这是默认值。

Always

提供商始终使用事务,即使是单实体操作也不例外。

Never

提供商从不使用事务。

在调用 SaveChanges()SaveChangesAsync() 方法之前,您可以在任何点设置此属性,如下示例所示。选择 SynchronousAsynchronous 标签页以查看相应的代码。

dbContext.Database.AutoTransactionBehavior = AutoTransactionBehavior.Never;
// This SaveChanges() call will not use a transaction
dbContext.Planets.Add(new Planet { Name = "Mars" });
dbContext.SaveChanges();
dbContext.Database.AutoTransactionBehavior = AutoTransactionBehavior.Never;
// This SaveChangesAsync() call will not use a transaction
dbContext.Planets.Add(new Planet { Name = "Mars" });
await dbContext.SaveChangesAsync();

您可以使用显式事务将多个 SaveChanges() 调用和其他操作分组为单个原子工作单元。要开始显式事务,调用 Database.BeginTransaction()Database.BeginTransactionAsync() 方法。执行操作后,调用 Commit()CommitAsync() 以完成事务。

以下示例通过将多个操作包装在单个事务中来展示此模式。如果任何操作抛出异常,提供商会自动回滚事务中的所有更改。选择 SynchronousAsynchronous 标签页,查看相应的代码。

using var transaction = dbContext.Database.BeginTransaction();
var planet = dbContext.Planets.First(p => p.Name == "Mercury");
planet.Name = "Mercury (Updated)";
dbContext.SaveChanges();
dbContext.Planets.Add(new Planet { Name = "Venus" });
dbContext.SaveChanges();
transaction.Commit();
await using var transaction = await dbContext.Database.BeginTransactionAsync();
var planet = await dbContext.Planets.FirstAsync(p => p.Name == "Mercury");
planet.Name = "Mercury (Updated)";
await dbContext.SaveChangesAsync();
dbContext.Planets.Add(new Planet { Name = "Venus" });
await dbContext.SaveChangesAsync();
await transaction.CommitAsync();

提示

手动回滚

此页面上的示例使用 using 声明来自动回滚事务并处置事务对象。要手动回滚事务,请在 catch 块中调用 Rollback()RollbackAsync() 方法。

您可以将 TransactionOptions 对象传递给 BeginTransaction()BeginTransactionAsync() 方法,以配置事务的读关注(read concern)、写关注(write concern)、读取偏好(read preference)和最大提交时间。

TransactionOptions 类具有以下属性:

属性
类型
说明

ReadConcern

ReadConcern

事务中第一个命令的读关注(read concern)。默认值为 null。如果您未设置此属性,则提供商会从会话的默认事务选项或 MongoClient 继承该值。

ReadPreference

ReadPreference

事务中读取操作的读取偏好(read preference)。事务必须从主节点 (primary node in the replica set)读取。默认值为 null。如果您未设置此属性,则提供商会继承会话的默认事务选项或 MongoClient的值。

WriteConcern

WriteConcern

commitTransactionabortTransaction 命令的写关注(write concern)。默认值是 null。如果您未设置此属性,提供商将从会话的默认事务选项或 MongoClient 继承值。

MaxCommitTime

TimeSpan?

允许单个 commitTransaction 命令运行的最长时间。默认值为 null。如果您未设置此属性,则提供商不会发送提交时间限制。

以下示例使用 MajorityReadConcern 开始事务。选择 SynchronousAsynchronous 标签页以查看相应的代码。

var options = new TransactionOptions(
readConcern: new Optional<ReadConcern>(ReadConcern.Majority)
);
using var transaction = dbContext.Database.BeginTransaction(options);
var planet = dbContext.Planets.First(p => p.Name == "Mercury");
planet.Name = "Mercury (Updated)";
dbContext.SaveChanges();
transaction.Commit();
var options = new TransactionOptions(
readConcern: new Optional<ReadConcern>(ReadConcern.Majority)
);
await using var transaction = await dbContext.Database.BeginTransactionAsync(options);
var planet = await dbContext.Planets.FirstAsync(p => p.Name == "Mercury");
planet.Name = "Mercury (Updated)";
await dbContext.SaveChangesAsync();
await transaction.CommitAsync();

要进一步了解读关注(read concern)、写关注(write concern)和读取偏好(read preference),请参阅 读关注(read concern)写关注(write concern)读取偏好(read preference)。

使用事务时应用以下限制和注意事项:

  • 仅当部署不支持自动事务时,才禁用自动事务。禁用自动事务可能会导致数据不一致,并阻止使用乐观并发。

  • MongoDB Server 不支持嵌套事务。如果在事务已经活跃时调用 BeginTransaction()BeginTransactionAsync(),则提供商会抛出 InvalidOperationException

  • MongoDB Server 不支持事务保存点。您必须提交或回滚整个事务。

  • 默认情况下,MongoDB Server 会中止运行超过 60 秒的任何事务,这受 transactionLifetimeLimitSeconds 服务器参数控制。此限制适用于隐式事务和显式事务。如果事务超过此时间限制,MongoDB Server 会中止事务,提供商会抛出错误。避免在事务中执行长时间操作,例如外部 API 调用。

  • 如果 MongoDB Server 发生网络中断或必须执行副本集选举,则可能会返回 TransientTransactionError 错误。在这些情况下,EF Core 提供商不会自动重试事务。要了解有关短暂事务错误和重试策略的更多信息,请参阅事务错误处理。

要了解有关本指南中概念的更多信息,请参阅以下资源:

要进一步了解本指南中使用的方法,请参阅以下 .NET API 文档: