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