El uso de transacciones desde .NET (SqlTransaction)

¿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

 

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();
    }
}

 

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

 

public class Article
{
    public int Id = 0;
    public string Content = string.Empty;
}
 

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

 

public void UpdateArticles(List<Article> 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;
    }
}
 

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.

Published jueves, 10 de julio de 2008 13:49 by Indigo

Comments

# El uso de transacciones desde .NET (SqlTransaction)

jueves, 10 de julio de 2008 13:57 by www.webeame.net

¿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

# re: El uso de transacciones desde .NET (SqlTransaction)

miércoles, 23 de julio de 2008 22:44 by Daniel Llanos

Hola, te tengo una consulta, que hacer para evitar que una transaccion bloquee a otra, en caso de que las transacciones involucren procesos mas complejos que necesiten mas tiempo de ejecución.

Gracias.

# re: El uso de transacciones desde .NET (SqlTransaction)

jueves, 24 de julio de 2008 8:04 by Indigo

La verdad es que habría que ver cuales son los procesos implicados. Una posibilidad es intentar meter todos los procesos de acceso a datos en la misma transacción aunque esto es muchos casos no es posibles. Otra posiblidad es aumentar el timeout de las operaciones, aunque esto puede traer acarrear otras consecuencias.

Leave a Comment

(required) 
(required) 
(optional)
(required) 
Powered by Community Server (Non-Commercial Edition), by Telligent Systems