Header Ads Widget

Transacciones en Entity Framework Core

Cada programador que trabaje con bases de datos SQL necesita conocer sobre transacciones. Y desde que se puede dar el caso en que la base de datos sea abstraída por un ORM como Entity Framework Core, es importante conocer como trabajar con transacciones utilizando las abstracciones disponibles.

Comportamiento por defecto

Por defecto, en EF Core, todos los cambios realizados en una simple llamada a SaveChanges son aplicados en una transacción (implicíta). Si los cambios fallan, la transacción se revierte y no se aplican los cambios a la base de datos. Solo si todos los cambios son exitosamente persistidos en la base de datos, la llamada a SaveChanges pueden completarse.
Este comportamiento nos ahorra muchos dolores de cabeza. No tendremos que preocuparnos de dejar la base de datos en un estado inconsistente.
using var context = new TiendaContext();
context.Lineas.Add(new Linea {
	ProductId = productId,
    Quantity = quantity
});

var stock = context.Stock.FirstOrDefault(s => s.ProductId == productId);
stockQuantity -= quantity;
context.SaveChanges();
Por ejemplo: si agregamos una línea de una factura y en el mismo alcance reducimos el stock, la llamada a SaveChanges aplicará ambos cambios dentro de una sola transacción. De esa forma podemos garantizar que la base de datos queda en un estado consistente.

Creando Transacciones con EF Core

Pero en algún momento necesitaremos tener más control sobre las transacciones cuando trabajemos con EF Core. Pues, se pueden crear transacciones de forma manual accediendo a una instancia de DBContext y llamando a BeginTransaction.
Por ejemplo: si tenemos varias llamadas a SaveChanges. En el escenario por defecto, ambas llamadas tienen su propia transacción. Esto deja la posibilidad en la que la segunda llamada a SaveChanges falle y deje la base de datos en un estado inconsistente.
using var context = new TiendaContext();
using var transaction = context.Database.BeginTransaction();
try
{
context.Lineas.Add(new Linea {
	ProductId = productId,
    Quantity = quantity
});
context.SaveChanges();
var stock = context.Stock.FirstOrDefault(s => s.ProductId == productId);
stockQuantity -= quantity;
context.SaveChanges();
//cuando se completen los cambios, serán aplicados a la base de datos
//la transacción se revertirá automáticamente cuando sea eliminada
//si algún comando falla
transaction.Commit();

}
catch(Exception)
{
	transaction.Rollback();
}
Para evitar ese problema, llamamos a BeginTransaction para iniciar de forma manual una nueva transacción de base de datos. Esto creará una nueva transacción y la devolverá al programa, de modo que podamos completar la transacción (Commit) cuando la operación esté completa. A la vez podemos agregar un bloque try-catch rodeando el código, de modo que se pueda revertir la transacción (Rollback) si hay alguna excepción.

Usar Transacciones Existentes con EF Core

Crear una transacción utilizando DbContext de EF Core no es la única opción. Se puede crear una instancia de SqlTransaction y pasarla a EF Core, de modo que los cambios aplicados con EF Core pueden ser completados dentro de la misma transacción.
using var sqlConnection = new SqlTransaction(connectionString);
sqlConnection.Open();
using var transaction = sqlConnection.BeginTransaction();
try
{
using var context = new TiendaContext();
context.UseTransaction(transaction);
context.Lineas.Add(new Linea {
	ProductId = productId,
    Quantity = quantity
});
context.SaveChanges();
var stock = context.Stock.FirstOrDefault(s => s.ProductId == productId);
stockQuantity -= quantity;
context.SaveChanges();
//cuando se completen los cambios, serán aplicados a la base de datos
//la transacción se revertirá automáticamente cuando sea eliminada
//si algún comando falla
transaction.Commit();

}
catch(Exception)
{
	transaction.Rollback();
}

Resumen

EF Core soporta las transacciones de base de datos y es muy fácil de trabajarlo.
Tenemos tres opciones disponibles:
  • Confiar en el comportamiento por defecto de las transacciones.
  • Crear una nueva transacción.
  • Utilizar una transacción existente.
En la mayoría de los casos podemos confiar en el comportamiento por defecto y no tenemos que pensar en ello. Tan pronto como necesitemos ejecutar diferentes llamadas a SaveChnages, podrías crear manualmente una transacción y controlarla por nosotros mismos.


Publicar un comentario

0 Comentarios