Overview
En esta guía, aprenderá a usar transacciones en sus aplicaciones de EF Core Provider. Una transacción encapsula una serie de operaciones de escritura. Si alguna operación falla, el proveedor revierte todos los cambios realizados en la transacción. Este comportamiento ayuda a garantizar la coherencia de los datos.
El proveedor EF Core admite los siguientes modos de transacción:
Transacciones implícitas: El proveedor envuelve automáticamente las llamadas a la
SaveChanges()y losSaveChangesAsync()métodos en una transacción.Consulte Configurar transacciones implícitas para obtener información sobre cómo configurar o deshabilitar este comportamiento.Transacciones explícitas: Se llama al
Database.BeginTransaction()método para iniciar manualmente una transacción, y luego se confirma o se revierte la transacción llamandoCommit()aRollback()o.
Datos de muestra
Los ejemplos de esta guía utilizan la colección planets de la base de datos sample_guides. Los documentos de esta colección utilizan la siguiente clase Planet como 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; } }
Esta colección es de los conjuntos de datos de muestra proporcionados por Atlas. Ver el Guía de inicio rápido para aprender a crear un clúster de MongoDB gratuito y cargar estos datos de ejemplo.
Requisitos y límites
Antes de utilizar las transacciones, tenga en cuenta los siguientes requisitos y limitaciones:
Las transacciones requieren una implementación de MongoDB Server que admita transacciones de varios documentos, como un conjunto de réplicas o un clúster fragmentado. Si su aplicación intenta iniciar una transacción en una implementación independiente, el proveedor generará un
NotSupportedExceptionerror. Para evitarlo, configureAutoTransactionBehavior.Nevercomo se describe en Configurar transacciones implícitas.El proveedor de EF Core no admite transacciones ambientales de .NET, como las creadas por
System.Transactions.TransactionScope. Si intenta usar una transacción ambiental, el proveedor generará una excepción. En su lugar, utilice transacciones implícitas o explícitas.
Transacciones implícitas
Por defecto, el proveedor de EF Core encapsula automáticamente SaveChanges() las SaveChangesAsync() llamadas y en una transacción cuando se ven afectadas varias entidades raíz. Una entidad raíz es un documento de nivel superior en una colección, a diferencia de un tipo propio o incrustado que se almacena dentro de otro documento.
El siguiente ejemplo inserta dos planetas en una sola llamada SaveChanges(). Dado que esta operación afecta a más de una entidad raíz, el proveedor la encapsula automáticamente en una transacción. Si alguna de las inserciones falla, el proveedor revierte ambos cambios.
Selecciona el Synchronous o la pestaña Asynchronous para ver el código correspondiente.
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 transacciones implícitas
La propiedad Database.AutoTransactionBehavior de su objeto DbContext controla cuándo el proveedor utiliza transacciones implícitas. Esta propiedad acepta un valor de la enumeración AutoTransactionBehavior, que contiene los siguientes valores:
Valor | Descripción |
|---|---|
| El proveedor utiliza una transacción solo cuando |
| El proveedor siempre utiliza una transacción, incluso para operaciones con una sola entidad. |
| El proveedor nunca utiliza una transacción. |
Puedes configurar esta propiedad en cualquier momento antes de llamar al método SaveChanges() o SaveChangesAsync(), como se muestra en el siguiente ejemplo. Selecciona la pestaña Synchronous o Asynchronous para ver el código correspondiente.
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();
Transacciones explícitas
Puedes usar transacciones explícitas para agrupar varias llamadas SaveChanges() y otras operaciones en una sola unidad de trabajo atómica. Para iniciar una transacción explícita, llama al método Database.BeginTransaction() o Database.BeginTransactionAsync(). Después de realizar las operaciones, llama a Commit() o CommitAsync() para completar la transacción.
El siguiente ejemplo ilustra este patrón al agrupar varias operaciones en una sola transacción. Si alguna operación genera una excepción, el proveedor revierte automáticamente todos los cambios de la transacción. Seleccione la pestaña Synchronous o Asynchronous para ver el código correspondiente.
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();
Tip
Retroceso manual
Los ejemplos de esta página utilizan una declaración using para revertir automáticamente las transacciones y liberar los objetos de transacción. Para revertir una transacción manualmente, llame al método Rollback() o RollbackAsync() en un bloque catch.
Configurar transacciones explícitas
Puede pasar un objeto TransactionOptions al método BeginTransaction() o BeginTransactionAsync() para configurar la preocupación de lectura, la preocupación de escritura, la preferencia de lectura y el tiempo máximo de confirmación para la transacción.
La clase TransactionOptions tiene las siguientes propiedades:
Propiedad | Tipo | Descripción |
|---|---|---|
|
| Lea la información sobre el primer comando de la transacción. El valor predeterminado es |
|
| Preferencia de lectura para operaciones de lectura en la transacción. Las transacciones deben leer desde el nodo principal. El valor predeterminado es |
|
| Escriba la preocupación para los comandos |
|
| Tiempo máximo permitido para la ejecución de un único comando |
El siguiente ejemplo inicia una transacción con un ReadConcern de Majority. Seleccione la pestaña Synchronous o Asynchronous para ver el código correspondiente.
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 obtener más información sobre la preocupación por la lectura, la preocupación por la escritura y la preferencia de lectura,consulte Preocupación por la lectura,Preocupación porla escritura yPreferencia de lectura.
Limitaciones de transacción
Las siguientes limitaciones y precauciones se aplican al utilizar transacciones:
Desactive las transacciones automáticas únicamente si su implementación no las admite. Deshabilitarlas puede provocar inconsistencias en los datos e impedir el uso de la concurrencia optimista.
MongoDB Server no admite transacciones anidadas. Si llama a
BeginTransaction()oBeginTransactionAsync()mientras ya hay una transacción activa, el proveedor lanza unInvalidOperationException.MongoDB Server no admite puntos de guardado de transacciones. Debe confirmar o revertir la transacción completa.
Por defecto, MongoDB Server cancela cualquier transacción que dure más de 60 segundos, según lo controla el parámetro
transactionLifetimeLimitSecondsdel servidor. Este límite se aplica tanto a las transacciones implícitas como a las explícitas. Si su transacción supera este límite de tiempo, MongoDB Server la cancela y el proveedor genera un error. Evite realizar operaciones de larga duración, como llamadas a API externas, dentro de una transacción.Si MongoDB Server experimenta una interrupción de red o debe realizar una elección de conjunto de réplicas, podría devolver un
TransientTransactionErrorerror. En estos casos, el proveedor de EF Core no reintenta automáticamente la transacción. Para obtener más información sobre los errores de transacción transitorios y las estrategias de reintento, consulte la sección sobre el manejo de errores de transacción.
Información Adicional
Para obtener más información sobre los conceptos de esta guía, consulte los siguientes recursos:
Para obtener más información sobre los métodos utilizados en esta guía, consulte la siguiente documentación de la API de .NET: