Overview
在本指南中,您可以学习;了解如何在 EF Core 提供程序应用程序中使用事务。ACID 事务包含一系列写入操作。如果ACID 事务中的任何操作失败,提供商将回滚ACID 事务中的所有更改。此行为有助于确保数据一致性。
EF Core 提供程序支持以下ACID 事务模式:
隐式事务:提供商会自动将对
SaveChanges()和SaveChangesAsync()方法的调用包装在ACID 事务中。请参阅配置隐式事务,学习;了解如何配置或禁用此行为。显式事务:调用
Database.BeginTransaction()方法手动开始ACID 事务,然后通过调用 或 提交或回滚ACIDCommit()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部署,例如副本集或分片集群。如果您的应用程序尝试在独立运行部署上启动ACID 事务,则提供商会抛出 。为防止这种情况,请按照配置隐式事务中的说明设立
NotSupportedExceptionAutoTransactionBehavior.Never。EF Core 提供程序不支持.NET环境事务,例如由
System.Transactions.TransactionScope创建的事务。如果尝试使用环境ACID 事务,提供商会引发异常。请改用隐式或显式事务。
隐式事务
默认下,当多个根实体受到影响时,EF Core 提供程序会自动将SaveChanges() 和 调用包装在ACIDSaveChangesAsync() 事务中。根实体是集合中的顶层文档,而不是存储在另一个文档中的自有或嵌入式类型。
以下示例将在一次 SaveChanges() 调用中插入两个行星。由于此操作会影响多个根实体,因此提供商会自动将其包装在ACID 事务中。如果任一插入失败,提供商将回滚这两个更改。
选择 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枚举的值,其中包含以下值:
值 | 说明 |
|---|---|
| 仅当 |
| 提供商始终使用ACID 事务,即使对于单实体操作也是如此。 |
| 提供商从不使用ACID 事务。 |
您可以在调用 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() 调用和其他操作群组到单个原子工作单元中。要启动显式ACID 事务,请调用 Database.BeginTransaction() 或 Database.BeginTransactionAsync() 方法。执行操作后,调用 Commit() 或 CommitAsync() 以完成ACID 事务。
以下示例通过将多个操作包装在单个ACID 事务中来展示此模式。如果任何操作抛出异常,提供商会自动回滚ACID 事务中的所有更改。选择 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 声明来自动回滚事务并处置ACID 事务对象。要手动回滚ACID 事务,请在 catch区块中调用 Rollback() 或 RollbackAsync() 方法。
配置显式事务
您可以将 TransactionOptions对象传递给 BeginTransaction() 或 BeginTransactionAsync() 方法,以配置ACID 事务的读关注(read concern)、写关注(write concern)、读取偏好(read preference)和最长提交时间。
TransactionOptions 类具有以下属性:
属性 | 类型 | 说明 |
|---|---|---|
|
| ACID 事务中第一个命令的读关注。默认值为 |
|
| ACID 事务中读取操作的读取偏好。事务必须从主节点 (primary node in the replica set)读取。默认值为 |
|
| 为 |
|
| 允许单个 |
以下示例启动一个 ReadConcern 为 Majority 的ACID 事务。选择 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)的更多信息,请参阅 读关注、写关注 和 读取偏好。
事务限制
使用事务时,以下限制和注意事项应用:
MongoDB Server不支持嵌套事务。如果在ACID 事务已处于活动状态时调用
BeginTransaction()或BeginTransactionAsync(),提供商会抛出InvalidOperationException。MongoDB Server不支持ACID 事务保存点。您必须提交或回滚整个ACID 事务。
默认下, MongoDB Server中止任何运行超过 60 秒的ACID 事务(由
transactionLifetimeLimitSeconds服务器参数控制)。此限制适用于隐式和显式事务。如果您的ACID 事务超过此时间限制, MongoDB Server将中止事务,并且提供商会抛出错误。避免在ACID 事务中执行长时间运行的操作,如外部API调用。如果MongoDB Server遇到网络中断或必须执行副本集选举,则可能会返回
TransientTransactionError错误。在这些情况下,EF Core 提供程序不会自动重试ACID 事务。要学习;了解有关暂时性ACID 事务错误和重试策略的更多信息,请参阅事务错误处理。
更多信息
要学习;了解有关本指南中概念的更多信息,请参阅以下资源:
要学习;了解有关本指南中使用的方法的更多信息,请参阅以下.NET API文档: