¿Qué pasa si en alguna en un procesos de actualización de datos de una base de datos falla a la mitad? Lo normal en la mayoría de los casos es deshacer todo lo que se ha hecho y dejar la base de datos como estaba. Este proceso sería muy complicado hacerlo a mano, por no decir imposible. Para esto están las transacciones, que de una manera muy fácil nos ayudan a resolver este tipo de problemas.
Esta forma de trabajar con las bases de datos, podemos hacerlo desde nuestro código o directamente en la base de datos (en procedimientos de almacenado). En este artículo sólo explicaré como hacerlo desde nuestro código.
El código para utilizar una transacción es el siguiente
[sourcecode language='csharp']public void ExecuteTransaction()
{
// Usamos la clase SqlConnectionStringBuilder para construir la cadena de conexión
SqlConnectionStringBuilder _connectionStringBuilder = new SqlConnectionStringBuilder();
_connectionStringBuilder.DataSource = @”.\SQLEXPRESS”;
_connectionStringBuilder.InitialCatalog = “prueba”;
_connectionStringBuilder.IntegratedSecurity = true;
// Creamos la conexión
using(SqlConnection _connection = new SqlConnection(_connectionStringBuilder.ConnectionString))
{
// Abrimos la conexión
_connection.Open();
// Abrimos una transación en la conexión abierta
SqlTransaction _transaction = _connection.BeginTransaction();
// Creamos el objeto SqlCommand y asignamos los datos
// a los parámetros
SqlCommand _command = new SqlCommand();
_command.Connection = _connection;
_command.CommandType = CommandType.StoredProcedure;
_command.CommandText = “ProcedimientoAlmacenado”; // Aqui va el nombre del procedimiento de almacenado
// Añadimos los parametros
_command.Parameters.AddWithValue(”@Par1″, “Par1″);
_command.Parameters.AddWithValue(”@Par2″, “Par2″);
// Asignamos la transacción al comando
_command.Transaction = _transaction;
try
{
// Ejecutamos el comando
_command.ExecuteNonQuery();
// Si todo ha ido bien hacemos un “Commit” de la transacción
_transaction.Commit();
}
catch(Exception ex)
{
// Si se produce un error, desahacemos la transaccion (Rollback)
_transaction.Rollback();
}
// No se hace falta cerrar la conexión porque está dentro de un “using”
_connection.Close();
}
}[/sourcecode]
Lo primero que hacemos es crear una cadena de conexión y abrir una conexión con ésta. Con esta conexión abierta creamos la transacción (con el método “BeginTransaction”) y ejecutamos nuestro procedimiento de almacenado. Si todo ha ido bien, hacemos un “Commit” de la transacción y si algo ha fallado hacemos un “Rollback”. Es importante hacer un ”Commit” ya que si no, el sistema hará un “Rollback” automáticamente al cerrar la conexión.
Tal y como está el código sólo estamos haciendo una llamada la base de datos, pero modificando el código ligeramente podemos ver su verdadero potencial. Supongamos que tenemos la siguiente clase
[sourcecode language='csharp']public class Article
{
public int Id = 0;
public string Content = string.Empty;
}
[/sourcecode]
Y tenemos un método que recibe una lista de artículos que tenemos que actualizar. Este método creará una transacción y la utilizará para actualizar cada artículo. El código sería como este
[sourcecode language='csharp']public void UpdateArticles(List articles)
{
SqlTransaction _transaction = CreateTransaction();
try
{
foreach (Article article in articles)
{
UpdateArticle(_transaction, article);
}
_transaction.Commit();
}
catch
{
_transaction.Rollback();
throw;
}
}
public void UpdateArticle(SqlTransaction transaction, Article article)
{
try
{
// Creamos el objeto SqlCommand y asignamos los datos
// a los parámetros
SqlCommand _command = new SqlCommand();
_command.Connection = transaction.Connection;
_command.CommandType = CommandType.StoredProcedure;
_command.CommandText = “UpdataArticle”; // Aqui va el nombre del procedimiento de almacenado
// Añadimos los parametros
_command.Parameters.AddWithValue(”@Id”, article.Id);
_command.Parameters.AddWithValue(”@Content”, article.Content);
// Asignamos la transacción al comando
_command.Transaction = transaction;
// Ejecutamos el comando
_command.ExecuteNonQuery();
}
catch
{
throw;
}
}
public SqlTransaction CreateTransaction()
{
try
{
// Usamos la clase SqlConnectionStringBuilder para construir la cadena de conexión
SqlConnectionStringBuilder _connectionStringBuilder = new SqlConnectionStringBuilder();
_connectionStringBuilder.DataSource = @”.\SQLEXPRESS”;
_connectionStringBuilder.InitialCatalog = “prueba”;
_connectionStringBuilder.IntegratedSecurity = true;
// Creamos la conexión
SqlConnection _connection = new SqlConnection(_connectionStringBuilder.ConnectionString);
// Abrimos la conexión
_connection.Open();
// Abrimos una transación en la conexión abierta
SqlTransaction _transaction = _connection.BeginTransaction();
return _transaction;
}
catch
{
throw;
}
}[/sourcecode]
Cómo hemos visto el uso de transacciones desde .NET es muy fácil y hace nuestras aplicaciones más robustas. Pero, ¿qué pasa cuando entran en juego varias bases de datos, o incluso varios gestores de base de datos (varios Sql Server)? Para eso tenemos el TransactionScope, que podemos ver en http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx, pero esto lo dejo para otro momento.
Tags: c#, sqlcommand, sqlconnection, sqltransaction