对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
See how MongoDB 9.0 delivers up to 2x higher throughput.
MongoDB Branding Shape
Register now >
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 提供的示例数据集。请参阅EF Core 提供商入门指南,了解如何创建免费的 MongoDB 集群并加载此示例数据。

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

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

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

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

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

选择 Synchronous 或 Asynchronous 标签页,查看相应的代码。

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() 方法之前,您可以在任何点设置此属性,如下示例所示。选择 Synchronous 或 Asynchronous 标签页以查看相应的代码。

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() 以完成事务。

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

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

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

MaxCommitTime

TimeSpan?

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

以下示例使用 Majority 的 ReadConcern 开始事务。选择 Synchronous 或 Asynchronous 标签页以查看相应的代码。

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 文档: