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:
Transações implícitas: O provedor automaticamente envolve chamadas para os métodos
SaveChanges()eSaveChangesAsync()em uma transação. Consulte Configurar transações implícitas para saber como configurar ou desabilitar esse comportamento.Transações explícitas: Você chama o método
Database.BeginTransaction()para iniciar manualmente uma transação e, em seguida, confirma ou reverte a transação chamandoCommit()ouRollback().
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; } }
Essa collection é dos conjuntos de dados de amostra fornecidos pelo Atlas. Consulte o guia Iniciar com o fornecedor principal da EF para saber como criar um cluster MongoDB gratuito e carregar esses dados de amostra.
Requisitos e limitações
Antes de usar transações, observe os seguintes requisitos e limitações:
As transações exigem uma implantação do MongoDB Server que suporte transações multidocumento, como um conjunto de réplicas ou cluster sharded. Se seu aplicativo tentar iniciar uma transação em uma implantação autônoma, o provedor lançará um
NotSupportedException. Para evitar isso, definaAutoTransactionBehavior.Neverconforme descrito em Configurar transações implícitas.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();
Para saber mais sobre read concern, write concern e preferência de leitura, consulte Read Concern, Write Concern e Preferência de leitura.
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.Se o MongoDB Server sofrer uma interrupção de rede ou precisar realizar uma eleição de conjunto de réplicas, ele poderá retornar um erro
TransientTransactionError. Nesses casos, o provedor EF Core não tenta novamente a transação automaticamente. Para saber mais sobre erros de transação transitórios e estratégias de repetição, consulte Tratamento de erros de transações.
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: