Overview
在本指南中,您可以学习如何在 EF Core 提供商应用程序中使用 事务。事务封装一系列写入操作。如果事务中的任何操作失败,提供商将回滚事务中的所有更改。此行为有助于确保数据一致性。
EF Core 提供商支持以下事务模式:
显式事务:您调用
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() 调用中插入两个行星。由于此操作影响多个根实体,因此提供商会自动将其包装在事务中。如果任何插入失败,提供商将回滚这两个更改。
选择 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 枚举的值,该枚举包含以下值:
值 | 说明 |
|---|---|
| 仅当 |
| 提供商始终使用事务,即使是单实体操作也不例外。 |
| 提供商从不使用事务。 |
在调用 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 类具有以下属性:
属性 | 类型 | 说明 |
|---|---|---|
|
| 事务中第一个命令的读关注(read concern)。默认值为 |
|
| 事务中读取操作的读取偏好(read preference)。事务必须从主节点 (primary node in the replica set)读取。默认值为 |
|
|
|
|
| 允许单个 |
以下示例使用 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 文档: