el Guille, la Web del Visual Basic, C#, .NET y más...

Transacciones simples con ADO.NET

 
Publicado el 09/Jul/2008
Actualizado el 10/Jul/2008
Autor: Guillermo 'guille' Som

En este artículo te explicaré cómo hacer transacciones simples por medio del objeto Connection y el objeto del tipo Transaction que devuelve el método BeginTransaction, además te daré unas indicaciones de cuándo es conveniente usar las transacciones.



 

Introducción:

Las transacciones es una forma de controlar si la actualización de datos en una base de datos se deben aceptar (normalmente si todo ha ido bien) o se deben descartar (si se produce un error), esto nos asegura que esa actualización de datos se haga solamente si todo ha ido como teníamos previsto.

¿Cuándo usar las transacciones?

Las transacciones tienen sentido si vamos a realizar varias tareas de actualización (ya sea realizando tareas de actualización, inserción o eliminación), por ejemplo si intervienen varias tablas, ya que podemos actualizar los datos de una tabla y al intentar actualizar otra, es cuando se produce un error, en este caso, podemos deshacer los cambios realizados en la primera tabla y cancelar toda la operación, de esta forma nos aseguramos que no queden datos "colgados" y que sólo tienen sentido si se realiza el proceso completo.
Aunque podemos usarlas siempre, para asegurarnos de que no habrá problemas, pero si solo vamos a leer datos, pues como que no tiene ninguna utilidad.

¿Cómo hacer una transacción?

Las transacciones se pueden hacer por código o directamente en el servidor (mediante procedimientos almacenados), aquí veremos solo cómo hacer esas transacciones por código y las que se conocen como transacciones locales, es decir, no transacciones distribuidas, para ese tipo de transacciones te recomiendo que veas las clases de System.Transactions, ya que aquí solo veremos las que podemos hacer por medio del objeto Transaction que devuelve el método BeginTransaction del objeto Connection (en estos ejemplos usaremos bases de datos de SQL Server 2005 Express).

Una vez que tenemos un objeto del tipo Transaction, debemos asignarlo a la propiedad del mismo nombre de cada uno de los comandos que intervendrán en la transacción, si no lo asignamos tendremos un bonito error, así que... una vez que usemos el método BeginTransaction del objeto Connection, debemos asignar esa transacción a cada comando que vayamos a usar.

Aceptar y/o cancelar una transacción

Una vez que tenemos todo bajo la "tutela" de un objeto Transaction, podemos aceptar todo los cambios realizados llamando al método Commit. Y si algo ha salido mal, o simplemente queremos cancelar todo lo que se ha hecho, llamaremos al método Rollback de ese objeto y como si nada hubiera pasado, vamos como se suele decir: aquí paz y después gloria.

 

Y esto es todo lo que tienes que saber sobre las transacciones, ves como no era tan complicado.

¿Cómo dices?
¿No te has enterado?
Pero si está claro, a ver... primero abres la conexión, creas el objeto Transaction mediante el método BeginTransaction de esa conexión, creas los comandos, le asignas a la propiedad Transaction de cada comando ese objeto que te habrá devuelto el método BeginTransaction (y que habrás guardado en una variable del tipo Transaction), usas esos comandos (que también tendrán que estar relacionados con el objeto Connection), y si todo ha ido bien, llamas al método Commit del objeto Transaction, y en caso de que haya algún error o quieras cancelar, simplemente llamas al método Rollback de ese mismo objeto Transaction, y ya está.
¿Cómo? ¿Que quieres un ejemplo? Y además lo quieres en Visual Basic y en C#, si es que... bueno... vale.

Un ejemplo que usa todo lo comentado (en Visual Basic y C#)

En este ejemplo usaremos un comando INSERT a una tabla y todo el proceso lo incluiremos en una transacción.
Se supone que estás usando la versión 2005 ó 2008 de los lenguajes, vamos que estamos usando las clases incluidas con la versión 2.0 (o superior) de .NET Framework.

Paso 1, Crear el objeto SqlConnetion.
En este ejemplo, accedo a una base de datos llamada Prueba que está en el servidor local y en la instancia SQLEXPRESS (si vas a usar otra base de datos, tendrás que cambiar los valores de la cadena de conexión).
La conexión la creo en un bloque Using para que se cierre adecuadamente (y se liberen los recursos) si se produce un error no controlado.

' La cadena de conexión
Dim csb As New SqlConnectionStringBuilder
With csb
    ' El servidor al que nos conectamos
    .DataSource = "(local)\SQLEXPRESS"
    ' La base de datos que vamos a usar
    .InitialCatalog = "prueba"
    ' Usamos la seguridad integrada
    .IntegratedSecurity = True
End With

' Creamos la conexión
' la ponemos dentro de Using para asegurarnos de que se cierre si hay errores
Using con As New SqlConnection(csb.ConnectionString)
    ' Abrimos la conexión
    con.Open()

 

// La cadena de conexión
SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder();
// El servidor al que nos conectamos
csb.DataSource = @"(local)\SQLEXPRESS";
// La base de datos que vamos a usar
csb.InitialCatalog = "prueba";
// Usamos la seguridad integrada
csb.IntegratedSecurity = true;

// Creamos la conexión
// la ponemos dentro de Using para asegurarnos de que se cierre si hay errores
using(SqlConnection con = new SqlConnection(csb.ConnectionString))
{
    // Abrimos la conexión
    con.Open();

 

Paso 2. Crear el objeto Transaction.
Simplemente asignamos a una variable del tipo SqlTransaction el valor devuelto por el método BeginTransaction del objeto que tiene la conexión.

' Creamos el objeto Transaction
Dim tran As SqlTransaction = con.BeginTransaction

 

// Creamos el objeto Transaction
SqlTransaction tran = con.BeginTransaction();

 

Paso 3. Creamos el objeto SqlCommand con el comando INSERT INTO adecuado (también podríamos llamar a un procedimiento almacenado). Se utilizan parámetros, ya que así es un poco más seguro y evitamos las temidas SQL-Injection o dicho de forma llana, evitamos que no las cuelen... o casi... ;-)))

' Creamos el comando de inserción con parámetros
' La tabla que usaré se llama Table1
Dim sInsert As String = _
    "INSERT INTO Table1 " & _
    "(Nombre, Correo, Fecha) " & _
    "VALUES (@Nombre, @Correo, @Fecha)"

' Creamos el objeto SqlCommand y asignamos los datos
' a los parámetros
Dim cmd As New SqlCommand(sInsert, con)

With cmd
    .Parameters.AddWithValue("@Nombre", txtNombre.Text)
    .Parameters.AddWithValue("@Correo", txtCorreo.Text)
    .Parameters.AddWithValue("@Fecha", txtFecha.Value)
End With

 

// Creamos el comando de inserción con parámetros
// La tabla que usaré se llama Table1
string sInsert = 
    "INSERT INTO Table1 " + 
    "(Nombre, Correo, Fecha) " + 
    "VALUES (@Nombre, @Correo, @Fecha)";

// Creamos el objeto SqlCommand y asignamos los datos
// a los parámetros
SqlCommand cmd = new SqlCommand(sInsert, con);

cmd.Parameters.AddWithValue("@Nombre", txtNombre.Text);
cmd.Parameters.AddWithValue("@Correo", txtCorreo.Text);
cmd.Parameters.AddWithValue("@Fecha", txtFecha.Value);

 

Paso 4. Asignamos la transacción al método Transaction del comando.
Esto siempre debemos hacerlo en todos los comandos que modifiquen los datos, si no lo asignamos, recibiremos un error.

' Asignamos la transacción al comando
cmd.Transaction = tran

 

// Asignamos la transacción al comando
cmd.Transaction = tran;

 

Paso 5. Ejecutamos el comando.
La llamada al método ExecuteNonQuery lo ponemos dentro de un Try/Catch, de esta forma, si todo va bien (no se produce error) llamamos al método Commit del objeto Transaction (la variable tran).
Y si se produce algún error deshacemos todo, llamando al método Rollback del objeto Transaction.

' Insertamos los datos
' lo pongo en un Try/Catch para detectar los errores
Try
    ' Ejecutamos el comando
    cmd.ExecuteNonQuery()

    ' Si llega aquí es que todo fue bien,
    ' por tanto, llamamos al método Commit
    tran.Commit()

    txtInfo.Text = "Se han actualizado los datos"

Catch ex As Exception
    ' Si hay error, desahacemos lo que se haya hecho
    tran.Rollback()

    txtInfo.Text = "ERROR: " & vbCrLf & ex.Message
End Try

 

// Insertamos los datos
// lo pongo en un Try/Catch para detectar los errores
try
{
    // Ejecutamos el comando
    cmd.ExecuteNonQuery();

    // Si llega aquí es que todo fue bien,
    // por tanto, llamamos al método Commit
    tran.Commit();

    txtInfo.Text = "Se han actualizado los datos";

}
catch(Exception ex)
{
    // Si hay error, desahacemos lo que se haya hecho
    tran.Rollback();

    txtInfo.Text = "ERROR: \r\n" + ex.Message;
}

 

Paso 6. Cerramos la conexión.
En realidad no es necesario, ya que al estar dentro de un bloque Using, se cerrará al llamar al método Dispose del objeto conexión.

    ' Cerramos la conexión,
    ' aunque no es necesario ya que al finalizar
    ' el using se cerrará
    con.Close()
End Using

 

    // Cerramos la conexión,
    // aunque no es necesario ya que al finalizar
    // el using se cerrará
    con.Close();
}

 

Ahora sí que sí... Espero que te sea de utilidad y sepas cómo usar las transacciones, aunque sea de forma simple.

Nos vemos.
Guillermo

P.S.
Si quieres ver qué cosas debes tener en cuenta si las actualizaciones las haces por medio del modo desconectado (con un DataAdapter y un DataTable o DataSet), mira esto: Transacciones simples con ADO.NET usando un DataAdapter.

 


Espacios de nombres usados en el código de este artículo:

System.Data.SqlClient
System.Data 



 


La fecha/hora en el servidor es: 23/12/2024 18:58:04

La fecha actual GMT (UTC) es: 

©Guillermo 'guille' Som, 1996-2024