Entity Framework emplea y facilita el uso de transacciones, como herramienta fundamental para mantener la integridad de los datos.
Una transacción es una unidad de trabajo que agrupa varias operaciones que deben ejecutarse de manera atómica, es decir:
O se ejecutan todas con éxito, o no se ejecuta ninguna.
Son uno de los mecanismos más importantes que tenemos para mantener la integridad de los datos.
Vamos a ver como gestionarlas con Entity Framework de automática o manual
Transacciones automáticas
Entity Framework Core utiliza transacciones internas automáticas por defecto. Cada vez que llamamos a SaveChanges()
o SaveChangesAsync()
, EF Core crea y envuelve esa operación dentro de una transacción.
Esto significa que si realizamos múltiples cambios en entidades y luego llamamos a SaveChanges()
, EF Core aplicará todo dentro de una sola transacción.
using (var context = new AppDbContext())
{
var cliente = new Cliente { Nombre = "Carlos" };
var pedido = new Pedido { Fecha = DateTime.Now };
context.Clientes.Add(cliente);
context.Pedidos.Add(pedido);
context.SaveChanges(); // Todo esto es una única transacción
}
Transacciones manuales
Entity Framework Core nos permite controlar de forma manual las transacciones a través de IDbContextTransaction
.
using (var context = new AppDbContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
var cliente = new Cliente { Nombre = "Ana" };
context.Clientes.Add(cliente);
context.SaveChanges();
var pedido = new Pedido { ClienteId = cliente.Id, Fecha = DateTime.Now };
context.Pedidos.Add(pedido);
context.SaveChanges();
transaction.Commit(); // Todo fue bien, se confirman los cambios
}
catch (Exception ex)
{
transaction.Rollback(); // Algo falló, se deshacen los cambios
Console.WriteLine($"Error: {ex.Message}");
}
}
}
Rollback al no confirmar la transacción
Si ocurre una excepción antes de llamar a Commit()
, EF Core revertirá la transacción al desechar el objeto (por ejemplo, al salir del using
).
try
{
using var tx = context.Database.BeginTransaction();
// operaciones...
tx.Commit(); // solo si todo salió bien
}
catch (Exception ex)
{
// rollback implícito al salir del bloque
Console.WriteLine("Error en la transacción.");
}
Transacciones con múltiples DbContext
En ocasiones, necesitamos usar varios DbContext
dentro de una misma transacción. Para ello, deben compartir la misma conexión:
using var connection = new SqlConnection(connectionString);
connection.Open();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer(connection)
.Options;
using var context1 = new AppDbContext(options);
using var context2 = new OtraDbContext(options);
using var transaction = connection.BeginTransaction();
context1.Database.UseTransaction(transaction);
context2.Database.UseTransaction(transaction);
try
{
context1.Algo.Add(new Entidad1());
context1.SaveChanges();
context2.OtraCosa.Add(new Entidad2());
context2.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
Transacciones anidadas con Savepoints
EF Core permite crear puntos de guardado dentro de una transacción para hacer rollback parcial:
await using var tx = await context.Database.BeginTransactionAsync();
try
{
// paso 1
context.Algo.Add(new EntidadA());
await context.SaveChangesAsync();
await tx.CreateSavepointAsync("Paso1");
// paso 2
context.Algo.Add(new EntidadB());
await context.SaveChangesAsync();
await tx.RollbackToSavepointAsync("Paso1");
await tx.CommitAsync();
}
catch
{
await tx.RollbackAsync();
}
¿Cuándo usar transacciones manuales en EF Core?
Aunque Entity Framework maneja transacciones automáticas con cada SaveChanges()
, hay situaciones donde nos conviene usar transacciones explícitas:
- Cuando necesitas agrupar varias llamadas a
SaveChanges()
en una sola unidad de trabajo. - Si necesitas hacer rollback parcial (usando savepoints).
- Cuando trabajas con múltiples
DbContext
o bases de datos. - Si combinas acceso con ADO.NET u otras operaciones externas.