Visão geral
Neste guia, você aprenderá a usar transações em seus aplicativos do provedor EF Core. Uma transação envolve uma série de operações de gravação. Se alguma operação na transação falhar, o provedor reverterá todas as alterações na transação. Esse comportamento ajuda a garantir a consistência dos dados.
O provedor do EF Core suporta os seguintes modos de transação:
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().
Dados de amostra
Os exemplos neste guia usam a coleção planets do banco de dados sample_guides . Os documentos nesta coleção utilizam a seguinte classe Planet como um modelo:
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.
Requisitos e limitações
Antes de usar transações, observe os seguintes requisitos e limitações:
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.O provedor EF Core não oferece suporte a transações ambiente .NET, como as criadas por
System.Transactions.TransactionScope. Se você tentar usar uma transação ambiente, o provedor lançará uma exceção. Em vez disso, use transações implícitas ou explícitas.
Transações implícitas
Por padrão, o provedor EF Core automaticamente encapsula as chamadas SaveChanges() e SaveChangesAsync() em uma transação quando várias entidades raiz são afetadas. Uma entidade raiz é um documento de nível superior em uma coleção, em oposição a um tipo de propriedade ou incorporado que é armazenado dentro de outro documento.
O exemplo a seguir insere dois planetas em uma única chamada SaveChanges(). Como esta operação afeta mais de uma entidade raiz, o provedor a envolve automaticamente em uma transação. Se qualquer inserção falhar, o provedor reverterá ambas as alterações.
Selecione a aba Synchronous ou Asynchronous para ver o código correspondente.
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();
Configurar transações implícitas
A propriedade Database.AutoTransactionBehavior em seu objeto DbContext controla quando o provedor usa transações implícitas. Esta propriedade aceita um valor da enumeração AutoTransactionBehavior, que contém os seguintes valores:
Valor | Descrição |
|---|---|
| O provedor usa uma transação somente quando |
| O provedor sempre usa uma transação, mesmo para operações de entidade única. |
| O provedor nunca usa uma transação. |
Você pode definir esta propriedade a qualquer momento antes de chamar o método SaveChanges() ou SaveChangesAsync(), conforme mostrado no exemplo a seguir. Selecione a aba Synchronous ou Asynchronous para ver o código correspondente.
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();
Transações explícitas
Você pode usar transações explícitas para agrupar várias chamadas SaveChanges() e outras operações em uma única unidade atômica de trabalho. Para iniciar uma transação explícita, chame o método Database.BeginTransaction() ou Database.BeginTransactionAsync(). Depois de realizar suas operações, chame Commit() ou CommitAsync() para concluir a transação.
O exemplo a seguir mostra esse padrão agrupando várias operações em uma única transação. Se alguma operação gerar uma exceção, o provedor reverterá automaticamente todas as alterações na transação. Selecione a aba Synchronous ou Asynchronous para ver o código correspondente.
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();
Dica
Rollback manual
Os exemplos nesta página usam uma declaração using para reverter automaticamente as transações e descartar os objetos de transação. Para reverter manualmente uma transação, chame o método Rollback() ou RollbackAsync() em um bloco catch.
Configurar transações explícitas
Você pode passar um objeto TransactionOptions para o método BeginTransaction() ou BeginTransactionAsync() para configurar o read concern, write concern, preferência de leitura e tempo máximo de commit para a transação.
A classe TransactionOptions tem as seguintes propriedades:
Propriedade | Tipo | Descrição |
|---|---|---|
|
| Read concern para o primeiro comando na transação. O valor padrão é |
|
| Preferência de leitura para operações de leitura na transação. As transações devem ser lidas do primário. O valor padrão é |
|
| Write concern para os comandos |
|
| Quantidade máxima de tempo para permitir que um único comando |
O exemplo a seguir inicia uma transação com um ReadConcern de Majority. Selecione a aba Synchronous ou Asynchronous para ver o código correspondente.
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.
Limitações de transação
As seguintes limitações e precauções se aplicam ao usar transações:
Desative as transações automáticas somente se sua implantação não as suportar. A desativação de transações automáticas pode causar inconsistências de dados e impede que você use a concorrência otimista.
O MongoDB Server não suporta transações aninhadas. Se você chamar
BeginTransaction()ouBeginTransactionAsync()enquanto uma transação já estiver ativa, o provedor lançará umInvalidOperationException.O MongoDB Server não oferece suporte a pontos de salvamento de transações. Você deve fazer commit ou rollback de uma transação inteira.
Por padrão, o MongoDB Server anula qualquer transação que seja executada por mais de 60 segundos, conforme controlado pelo parâmetro do servidor
transactionLifetimeLimitSeconds. Este limite se aplica a transações implícitas e explícitas. Se sua transação exceder esse limite de tempo, o MongoDB Server a anulará e o provedor gerará um erro. Evite realizar operações de longa duração, como chamadas de API externas, dentro de uma transação.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.
Informações adicionais
Para saber mais sobre os conceitos deste guia, consulte os seguintes recursos:
Para saber mais sobre os métodos usados nesta guia, consulte a seguinte documentação da API .NET: