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: Coherencia: Una transaccin debe ser una unidad atmica de trabajo, o se hace todo o no se hace nada. 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