Transacción (Transaction): conjunto de operaciones SQL que se ejecutan de forma atómica. O bien todo se confirma mediante Commit, o bien todo se deshace si ocurre un fallo con Rollback, cumpliendo las propiedades ACID.
En .NET Framework/.NET se implementa mediante SqlTransaction, componente exclusivo para SQL Server.
Puntos clave del flujo:
- Abrir la conexión de base de datos
conn.Open()- Iniciar la transacción a través de la conexión
conn.BeginTransaction()- Asociar el objeto de transacción a
SqlCommand.Transaction- Ejecutar varias sentencias SQL por lotes
- Sin excepciones:
tx.Commit(); capturar excepciones:tx.Rollback()
Ejemplo
El código original no incluye la lógica de deshacer dentro del bloque catch. Aquí tienes la versión completa lista para ejecutar:
public static int ExecuteSqlTran(List<string> SQLStringList)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
SqlTransaction tx = conn.BeginTransaction();
cmd.Transaction = tx;
try
{
int count = 0;
for (int n = 0; n < SQLStringList.Count; n++)
{
string strsql = SQLStringList[n];
if (strsql.Trim().Length > 1)
{
cmd.CommandText = strsql;
count += cmd.ExecuteNonQuery();
}
}
tx.Commit(); // Si todo sale bien, confirmamos la transacción
return count;
}
catch (Exception ex)
{
tx.Rollback(); // Si falla alguna sentencia, deshacemos todos los cambios
throw ex; // Volvemos a lanzar la excepción para que la capa superior detecte el error
}
}
}Lenguaje del código: C# (cs)
Esta implementación concatena cadenas SQL directamente y expone el sistema a inyecciones SQL. Es útil para entender cómo funcionan las transacciones, pero en proyectos reales debes optar siempre por las consultas parametrizadas.
Ejemplo de transacción con consultas parametrizadas
Insertamos registros en bucle y usamos SqlParameter para prevenir inyecciones. Detalles importantes:
- Al final de cada iteración llama a
sqlCom.Parameters.Clear()para borrar parámetros antiguos y evitar errores por acumulación - Reutilizamos la misma instancia de
SqlCommand, solo modificamos CommandText y sus parámetros
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = conn;
SqlTransaction st = conn.BeginTransaction();
sqlCom.Transaction = st;
int res = 0;
try
{
for (int i = 0; i < 10; i++)
{
sqlCom.CommandText = "INSERT INTO [dbo].[Article]([Title])VALUES(@Title)";
SqlParameter param = new SqlParameter("@Title", SqlDbType.NVarChar, 250);
param.Value = i + "Este es un nuevo título" + DateTime.Now;
sqlCom.Parameters.Add(param);
res += sqlCom.ExecuteNonQuery();
sqlCom.Parameters.Clear(); // Limpiamos parámetros para la siguiente vuelta
}
st.Commit();
}
catch (Exception)
{
st.Rollback(); // Si salta una excepción, revertimos todo
}Lenguaje del código: C# (cs)
1. Reglas obligatorias que debes cumplir
- La llamada a
BeginTransaction()debe hacerse después de abrir la conexión con Open - Es obligatorio asignar la propiedad
TransactionenSqlCommand, de lo contrario se lanzará una excepción - Tras ejecutar
Commit()oRollback()la transacción finaliza automáticamente, no puedes invocarlos de nuevo
Otros apuntes
- En .NET Core/.NET 5+, se recomienda usar la sentencia
usingpara liberar automáticamente el objetoSqlTransaction - Para procesar lotes muy grandes de sentencias SQL, valora el uso de
SqlBulkCopy, rinde mejor que ejecutar ExecuteNonQuery dentro de bucles SqlTransactionno sirve para transacciones que abarquen varias bases de datos. En esos casos necesitas transacciones distribuidas medianteTransactionScope
SqlTransaction transacciones de base de datos