Está en la página 1de 9

Las transacciones con .

NET

Jos Garca

Las transacciones con .NET


Concepto de transaccin: Es una secuencia de operaciones realizadas como una sola unidad de trabajo.
Las propiedades de las transacciones se las conoce como ACID: Atomicidad, Coherencia, Aislamiento,
Durabilidad.
Atomicidad:

Una transaccin debe ser una unidad atmica de trabajo, o se hace todo o no se hace nada.

Coherencia:

Debe dejar los datos en un estado coherente luego de realizada la transaccin.

Aislamiento: Las modificaciones realizadas por transacciones son tratadas en forma independiente,
como si fueran un solo y nico usuario de la base de datos.
Durabilidad: Una vez concluida la transaccin sus efectos son permanentes y no hay forma de
deshacerlos.
Ahora, que tiene que ver esto con mis datos?? Pues bien, todas las instrucciones que normalmente
escribo en los procedimientos son update, insert, delete de una o ms tablas y si no uso procedimientos
almacenados uso comandos desde mi programa; pues bien, entonces sera algo as:
Update Persona set Sueldo=sueldo * 1. 5
Update Grupos set estado=1 where estado=0

Pero nos encontramos con un problema, que las dos sentencias deben hacerse siempre unidas, como si
fueran una sola pues perteneces a una actualizacin de sueldos. Pero que pasa si se realiza la primera
operacin y no la segunda. Huy! Que rabia, o que pasa si solo se realiza parte de la primera, ms Huy!
Pues no se sabe hasta que punto se hizo o no se hizo nada, pues nada hay que me garantice esto. Aqu
surgen las transacciones.
Begin Tran
Update Persona set Su eldo=sueldo * 1.5
Update Grupos set estado=1 where estado=0
Commit Tran

Al encerrar en una transaccin decimos que se realice todo o no se realice nada (atomicidad) pues si surge
algn error se deshace todo lo realizado anteriormente en la transaccin.

Pag. 1/9

Las transacciones con .NET

Jos Garca

Ahora desde vb.NET


(Comandos independientes)
Teniendo una base de datos llamada Ejemplo y una tabla de nombre persona que tiene los siguientes
campos
Codigo
varchar(10)
Nombres
varchar(50)
Sueldo
int
Estado
varchar(1)
En un formulario que posee un botn escribimos lo siguiente en el evento clic del botn, sin olvidarse
hacer antes el imports a system.data.sqlClient:
Dim Conn As SqlConnection = New SqlConnection("Data Source=INFORM77;Initial Catalog=EJEMPLO;User Id=sa")
Conn.Open()
Try
Dim Comando As New SqlClient.SqlCommand("INSERT INTO PERSONA (codigo, nombres, sueldo, estado)
Values('JG1','JOSE',200,'A')", Conn)
Comando.ExecuteNonQuery()
Comando = New SqlClient.SqlCommand("INSERT INTO PERSONA (codigo, nombres, sueldo, estado)
Values('JG2','LUIS',180,'B')", Conn)
Comando.ExecuteNonQuery()
Comando = New SqlClient.SqlCommand("INSERT INTO PERSONA (codigo, nombres, sueldo, estado)
Values('JG3','PEDRO',400,'A')", Conn)
Comando.ExecuteNonQuery()
Catch ex As Exception
MsgBox(ex.Message)
End Try
Conn.Close()
MsgBox("Datos Ingresados")

Como vemos aadiremos en la tabla persona 3 registro, pero si quisiramos convertirle a transaccin para
que se ejecuten todos o ninguno si encuentra un error deberamos hacer lo siguiente.

Fjese en los datos para el campo cdigo pues ahora son tr1, tr2, tr3.
Dim Conn As SqlConnection = New SqlConnection("Data Source=INFORM77;Initial Catalog=EJEMPLO;User Id=sa")
Conn.Open()
Dim myTrans As SqlTransaction
Dim Comando As SqlClient.SqlCommand
myTrans = Conn.BeginTransaction()
Try
Comando = New SqlClient.SqlCommand("INSERT INTO PERSONA (codigo, nombres, sueldo, estado)
Values('tr1','JOSE',200,'A')", Conn)
Comando.Transaction = myTrans
Comando.ExecuteNonQuery()
Comando = New SqlClient.SqlCommand("INSERT INTO PERSONA (codigo, nombres, sueldo, estado)
Values('tr2','LUIS',180,'B')", Conn)
Comando.Transaction = myTrans
Comando.ExecuteNonQuery()
Comando = New SqlClient.SqlCommand("INSERT INTO PERSONA (codigo, nombres, sueldo, estado)
Values('tr3','PEDRO',400,'A')", Conn)
Comando.Transaction = myTrans
Comando.ExecuteNonQuery()
myTrans.Commit()
MsgBox("Datos Ingresados")
Catch ex As Exception
myTrans.Rollback()
MsgBox(ex.Message)
End Try
Conn.Close()

Pag. 2/9

Las transacciones con .NET

Jos Garca

Ahora al ejecutar insertar los nuevos 3 registros sin novedad, pero lo har usando transacciones para lo
cual iniciamos la transaccin con myTrans = Conn.BeginTransaction() y finalizamos con myTrans.Commit() y
en caso de algn error ejecutamos myTrans.Rollback() para cancelar todo lo realizado en la transaccin.
Para comprobar que la transaccin se cancela al encontrar un error cambiamos los valores del campo
cdigo por tr1 por ab1 y tr2 por ab2, dejando tr3 en el tercer registro pues as dar un error de clave
duplicada porque ya existe un registro con esta clave previamente grabada.
Al ejecutar nos saldr un mensaje que indica que existi una violacin de primary key. Si verificamos los
datos, no habr ningn registro aadido pues como est en una transaccin o se agregan todos o no se
agrega ninguno.

Pag. 3/9

Las transacciones con .NET

Jos Garca

(Ahora con DataSets)


En un formulario tenemos 2 botones Grabar y LeerDatos adems de un DataGrid asi:

Ahora vamos a crear una clase llamada persona que ejecute las dos acciones de los botones, aqu est el
cdigo:
Imports System.Data.SqlClient
Public Class Persona
Public Function Recuperar() As DataSet
'definimos la coneccion
Dim Conn As SqlConnection = New SqlConnection("Data Source=INFORM77;Initial Catalog=EJEMPLO;User
Id=sa")
'creamos el data adapter con la instruccion select a recuperar
Dim adapter As New SqlDataAdapter("Select * from PERSONA", Conn)
'abrimos la coneccion
Conn.Open()
'creamos el dataset donde recuperaremos los datos de la base de da tos
Dim ds As DataSet = New DataSet
'recuperamos los datos a travez del adapter
adapter.Fill(ds, "PERSONA")
'retornamos los datos
Return ds
End Function
Public Sub Grabar(ByVal ds As DataSet)
'definimos la coneccion
Dim Conn As SqlConnection = New SqlConnection("Data Source=INFORM77;Initial Catalog=EJEMPLO;User
Id=sa")
'abrimos la coneccion
Conn.Open()
'creamos el data adapter con la instruccion select a recuperar
Dim adapter As New SqlDataAdapter("Select * from PERSONA", Conn)
'Creamos e inicimos la transaccion
Dim Tran As SqlTransaction = Conn.BeginTransaction
'asignamos la transaccion al comando Select del adapter
adapter.SelectCommand.Transactio n = Tran
'construimos los demas comandos del adapter (DELETE, INSERT, UPDATE)
Dim X As New SqlCommandBuilder(adapter)
Try
'Actualizamos el DataSet
adapter.Update(ds, "PERSONA")
'Confirmamos la tra nsaccion
Tran.Commit()
'mensaje final
MsgBox("Datos grabados con xito")
Catch Ex As SqlException
'variable para el mensaje

Pag. 4/9

Las transacciones con .NET

Jos Garca

Dim men As String


'configuracion del mensaje de acuerdo al numero de error devuelto por la MRDB
If ex.Number = 8152 Then
men = "Existen datos demasiados extensos, corrija el problema y vuelva a intentar"
ElseIf ex.Number = 2627 Then
If ex.Message.IndexOf("PRIMARY") <> -1 Then
men = "Error por intentar grabar valores duplicados en campos clave, corrija el
problema y vuelva a intentar"
ElseIf ex.Message.IndexOf("UNIQUE") <> -1 Then
men = "Error por intentar grabar valores duplicados en campos de valores nicos,
corrija el problema y vuelva a intentar"
Else
men = "Error general en la base de datos"
End If
ElseIf ex.Number = 515 Then
men = "Algunos datos no han sido ingresados y son necesario para completar la operacin,
corrija el problema y vuelva a intentar"
Else
men = "Error general en la base de datos"
End If
'cancelamos la transaccion
Tran.Rollback()
'Indicamos el mensaje
Throw New Exception(men)
Catch Ex As DBConcurrencyException
'cancelamos la transaccion
Tran.Rollback()
'Indicamos el mensaje
Throw New Exception("Lo siento, los datos fueron actualizados por otro usuario")
Catch Ex As Exception
'Indicamos el mensaje
Throw New Exception("Error: " & EX.Message)
End Try
End Sub
End Class

Luego en el formulario creado con los botones y el grid escribimos el siguiente cdigo
Botn Leer
Dim dt As New Persona
Grid.DataSource = dt.Recuperar

Botn Grabar
Dim dt As New Persona
Try
dt.Grabar(Grid.DataSource)
Grid.DataSource = dt.Recuperar
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try

Load_Form (Carga del formulario)


Dim dt As New Persona
Grid.DataSource = dt.Recuperar

De esta forma podemos grabar y leer los datos del grid en forma transaccional, es decir que si por algn
motivo existiese un error se cancelarn todas las actualizaciones. Adems esto nos es bien recomendable
cuando existe concurrencia de varios usuarios sobre el mismo grupo de registros pues graba solo los
registros actualizados del primer usuario que ejecuta el grabar y para los dems usuarios no graba ningn
registro, que si no se lo hace en forma transaccional grabara una parte de los registros y otros no.
Para esto hay que tomar en cuenta que el manejo de transacciones debe hacerse en capa de reglas de
negocio (nunca en la capa UI o en la de Datos).

Pag. 5/9

Las transacciones con .NET

Jos Garca

Usando D.T.C.
Hasta aqu hemos formado transacciones a nivel de base de datos y desde vb.NET su manejo cuando
usamos comandos o cuando usamos Datasets. Ahora vamos un paso ms all, usaremos DTC
(Coordinador de Transacciones Distribuidas), aprovechando el uso de COM+ y aprenderemos a
interactuar desde .NET.
Veamos un escenario. En un banco yo tengo un Depsito configurado como una transaccin pues al
depositar yo actualizo y creo registros en alrededor de 6 tablas. As mismo yo tengo un Retiro
configurado como otra transaccin y tambin actualizo y creo registros en 6 tablas ms. Pero que pasa
cuando deseo hacer otro objeto llamado Traspaso de Dinero en donde est involucrado un retiro y un
depsito. Pues, se complica la cosa porque son dos transacciones diferentes y una transaccin no puede
estar embebida en otra transaccin y pero an si complicamos el escenario cuando el depsito se realiza
en SQL y el retiro el Oracle. Huy! Como hago una transaccin que al hacer Rollback deje en su esto
inicial, pues esto si que est difcil, pues cuando se hacer una transaccin que encierra mas transacciones
lo visto hasta ahora casi casi no podemos emplear al menos que seamos muy bueno para el uso de
bandera de estado y para interoperar entre sistemas.
Para esto hay que crear una clase especial que cumpla ciertas caractersticas:
? Debe tener nombre seguro (Strong Name)
? Debe poseer dentro del AssemblyInfo las etiquetas
<Assembly: ApplicationName("Ejemplo_COM _NET")>
<Assembly: AssemblyKeyFile("c: \key\demo.snk")>

?
?

Debe heredar de ServicedComponent


Antes del nombre de la clase debe escribirse
<Transaction(TransactionOption.Required)> Public class Persona

Nota: Todos los mtodos de la clase se vuelven transaccionales, para ello hay que escribir
<AutoComplete()> antes de la declaracin de cada uno. Esto significa que cuando el mtodo termina sin
novedad enva un mensaje de terminacin y cuando termina por algn error ste enva automticamente al
DTC un mensaje de error para que cancele las operaciones.
Luego de todo hay que registrar la clase en COM+ usando Regsvcs.exe, o al usar la primera vez lo hace
automticamente pero si se tiene permisos de administrador.
Pasemos a transformar nuestra cdigo de actualizacin del DataSet en una clase COM+.
Nos creamos un proyecto nuevo dentro de la solucin que tenemos.
Luego nos creamos un par de llaves (pblica y privada para hacer de nuestro nuevo assembly uno con
nombre seguro), para esto salimos al smbolo del sistema de visual studio y nos creamos un directorio en
la raiz de C:\ (solo por facilidad)
Md key
crea un directorio llamado key
Cd key
cambia al Nuevo directorio
Sn k demo.snk
crea el archive que contiene las klaves
Creamos primero una referencia a: System.EnterpriseServices
Luego Ingresamos al archivo AssemblyInfo de nuestro nuevo proyecto y escribimos el imports a la
referencia anterior como primera lnea.
Imports System.EnterpriseServices

Pag. 6/9

Las transacciones con .NET

Jos Garca

Luego en las etiquetas de los atributos del ensamblado hay que escribir los 2 atributos exigidos para lo
que necesitamos:
<Assembly: ApplicationName("Ejemplo_COM _NET")>
<Assembly: AssemblyKeyFile("c: \key\demo.snk")>

El ApplicationName es el nombre con el que se registrar en el DTC


El AssemblyKeyFile indica el nombre del archivo generado con las claves pblica y privada usadas para
crear un assembly con nombre seguro.
La clase quedara tal como esta en el siguiente cdigo:
Imports System.EnterpriseServices
Imports System.Data.SqlClient
<Transaction(TransactionOption.Required)> Public Class GrabandoCom
Inherits ServicedComponent
<AutoComplete()> Public Sub Grabando(ByVal ds As DataSet)
'definimos la coneccion
Dim Conn As SqlConnection = New SqlConnection("Dat a Source=INFORM77;Initial Catalog=EJEMPLO;User
Id=sa;Password=''")
'abrimos la coneccion
Conn.Open()
'creamos el data adapter con la instruccion select a recuperar
Dim adapter As New SqlDataAdapter("Select * from PERSONA", Conn)
'construimos los dems comandos del adapter (DELETE, INSERT, UPDATE)
Dim X As New SqlCommandBuilder(adapter)
Try
'Actualizamos el DataSet
adapter.Update(ds, "PERSONA")
Catch Ex As SqlException
'variable para el mensaje
Dim men As String
'configuracion del mensaje de acuerdo al numero de error devuelto por la MRDB
If ex.Number = 8152 Then
men = "Existen datos demasiados extensos, corrija el problema y vuelva a intentar"
ElseIf ex.Number = 2627 Then
If ex.Message.IndexOf("PRIMARY") <> -1 Then
men = "Error por intentar grabar valores duplicados en campos clave, corrija el
problema y vuelva a intentar"
ElseIf ex.Message.IndexOf("UNIQUE") <> -1 Then
men = "Error por intentar grabar valores duplicados en campos de valores nicos,
corrija el problema y vuelva a intentar"
Else
men = "Error general en la base de datos"
End If
ElseIf ex.Number = 515 Then
men = "Algunos datos no han sido ingresados y son necesario para completar la operacin,
corrija el problema y vuelva a intentar"
Else
men = "Error general en la base de datos"
End If
Throw New Exception(men)
Catch Ex As DBConcurrencyException
Throw New Exception("Lo siento, los datos fueron actualizados por otro usuario")
Catch Ex As Exception
Throw New Exception("Error: " & EX.Message)
End Try
End Sub
End Class

Como se puede observar, no existe movimiento ni cdigo de transacciones pues esto se encargar
automticamente del DTC.
Luego en el proyecto de anterior donde tenamos la clase persona cambiamos la programacin, creando
antes una referencia al nuevo proyecto que acabamos de escribir.
El cdigo del mtodo grabar se reemplazo por este:
Pag. 7/9

Las transacciones con .NET

Jos Garca

Public Sub Grabar(ByVal ds As DataSet)


Dim dtc As New Datos.GrabandoCom
Try
dtc.Grabando(ds)
Catch ex As Exception
Throw New Exception(ex.Message)
End Try
End Sub

Aqu se crea una variable de tipo Datos.GrabandoCom que es el nombre del proyecto seguido del mtodo
GrabandoCom.
Eso es todo, ahora las transacciones son automticas y quien es encargado es el DTC. Puede estar las
conexiones a varias bases de datos de diferente o igual tipo ms todo se har Commit o Rollback, todo
como una sola y nica transaccin con todas las prestaciones de COM+.
Luego de ejecutar la pantalla que contiene el Grid se puede comprobar que las transacciones si se ejecutan
y es ms, al ver el monitor de COM+ comprobamos que todo est Ok.
Para ver que se ha registrado nuestra dll entraremos por:
? Inicio, Panel de Control, Herramientas Administrativas, Servicios de Componentes
? Raiz de Consola, Servicios de Componentes, Equipos, Mi PC, Aplicaciones COM+
o Alli debe estar nuestra aplicacin registrada

Y ms abajo en estadsticas de transacciones se puede ver el registro de cuantas transacciones se han


registrado, tanto las concluidas bien como las no concluidas.

Pag. 8/9

Las transacciones con .NET

Jos Garca

Espero haber realizado un pequeo ejemplo para su introduccin en el uso de DTC, una herramienta
potente pero de poco uso y ahora visto desde .NET.

Saludos cordiales,

Jos G. Garca A.
Aerosoftware
Jefe de Sistemas

Pag. 9/9

También podría gustarte