Overview
在本指南中,您可以学习如何在 EF Core 提供商应用程序中使用 事务。事务封装一系列写入操作。如果事务中的任何操作失败,提供商将回滚事务中的所有更改。此行为有助于确保数据一致性。
EF Core 提供商支持以下事务模式:
Implicit transactions: The provider automatically wraps calls to the
SaveChanges()andSaveChangesAsync()methods in a transaction. See Configure Implicit Transactions to learn how to configure or disable this behavior.Explicit transactions: You call the
Database.BeginTransaction()method to manually begin a transaction, then commit or roll back the transaction by callingCommit()orRollback().
样本数据
本指南中的示例使用 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; } }
This collection is from the sample datasets provided by Atlas. See the Get Started with the EF Core Provider guide to learn how to create a free MongoDB cluster and load this sample data.
要求和限制
在使用事务之前,请注意以下要求和限制:
Transactions require a MongoDB Server deployment that supports multi-document transactions, such as a replica set or sharded cluster. If your application tries to start a transaction on a standalone deployment, the provider throws a
NotSupportedException. To prevent this, setAutoTransactionBehavior.Neveras described in Configure Implicit Transactions.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();
To learn more about read concern, write concern, and read preference, see Read Concern, Write Concern, and Read Preference.
事务限制
使用事务时应用以下限制和注意事项:
仅当部署不支持自动事务时,才禁用自动事务。禁用自动事务可能会导致数据不一致,并阻止使用乐观并发。
MongoDB Server 不支持嵌套事务。如果在事务已经活跃时调用
BeginTransaction()或BeginTransactionAsync(),则提供商会抛出InvalidOperationException。MongoDB Server 不支持事务保存点。您必须提交或回滚整个事务。
默认情况下,MongoDB Server 会中止运行超过 60 秒的任何事务,这受
transactionLifetimeLimitSeconds服务器参数控制。此限制适用于隐式事务和显式事务。如果事务超过此时间限制,MongoDB Server 会中止事务,提供商会抛出错误。避免在事务中执行长时间操作,例如外部 API 调用。If MongoDB Server experiences a network interruption or must perform a replica set election, it might return a
TransientTransactionErrorerror. In these cases, the EF Core Provider doesn't automatically retry the transaction. To learn more about transient transaction errors and retry strategies, see Transaction Error Handling.
更多信息
要了解有关本指南中概念的更多信息,请参阅以下资源:
要进一步了解本指南中使用的方法,请参阅以下 .NET API 文档: