Colaboraciones en el Guille

Programación en 3 capas (II)

Transacciones

 

Fecha: 18/Oct/2005 (17/10/05)
Autor: Tomás Martín (tomasmm@msn.com)  

 


Resumen

Implementar transacciones en las que intervienen varias entidades de la capa de negocio en una aruitectura de tres capas.

Introducción

Basándonos en la arquitectura propuesta en el artículo anterior muestro una posible solución a la implementación de transacciones en las que intervienen varias entidades de negocio que heredan de dataset fuertemente tipados.

La principal dificultad estriba en poder utilizar las mismas entidades de negocio con o sin transacción. Veamos un ejemplo para intentar clarificarlo: supongamos un proceso de introducción de facturas en el que al grabar la factura se modifican datos en las entidades 'cliente' y 'artículo' (entre otros). Es obvio que estas entidades deben ser subceptibles de  ser manipuladas independientemente en sus procesos de mantenimeinto respectivos (CRUD) pero tambien forman parte del proceso de grabación de 'factura'. Si falla la grabación de la 'factura' por cualquier razón no deberia realizarse ninguna acción en las entidades 'cliente', 'artículo', etc. Pues para eso están las transacciones.

Muy bien, pero ¿como hacerlo?.

La primera solución podría ser duplicar dentro del la entidad 'factura' el código de las entidades 'artículo', 'cliente', etc (tablas, dataAdapters, codigo de verificación, etc). Esto sería utilizar la fuerza bruta y tener que realizar los cambios y actualizaciones varias veces. Yo, como programador vago que soy, no acepto este método. Va contra mis principos escribir dos veces la misma cosa.

¿Entonces? Vamos a considerar 'factura' como una entidad de entidades (desde ahora SuperEntidad) y que sea esta la que se encargue de decirle a las entidades que contiene 'chicas, os toca trabajar juntas'. Veamos como.

(El ejemplo accede a la base de datos pubs de SQL.)

SuperEntidad

En el código de ejemplo del artículo anterior disponiamos de las entidades 'Employees' y 'jobs' y queremos una Superentidad  que contenga una (o varias) instancias de ellas. Pues nada, creamos un componente y le implementamos propiedades que sean del tipo 'EntidadEmployees' y 'EntidadJobs'.

Dim MiEntidadJobs As DCE.Negocio.EntidadJobs
Public Property SE_Jobs() As DCE.Negocio.EntidadJobs
    Get
        Return Me.MiEntidadJobs
    End Get
    Set(ByVal Value As DCE.Negocio.EntidadJobs)
        Me.MiEntidadJobs = Value
    End Set
End Property
(De la misma manera para 'EntidadEmployyes.)

La SuperEntidad tambien tendrá métodos para Cargar y Guardar. En el primero no es necesaria transacción, unicamente llamará a los métodos correspondientes de las entidades .

Public Sub Cargar()
    Me MiEntidadEmployees.Cargar()
    Me MiEntidadJobs.Cargar()
End Sub

El método 'Guardar' debe ser el que se encargue de lanzar la transacción. Primer problema: desde la capa de negocio no debo tener visibilidad de objetos que corresponden a la capa de datos, como es el caso de la conexión asociada a la transacción. Pues vale, ya me crearé mi objeto transacción en mi capa de datos, mientras tanto voy a usar los interfaces IDbTransaction e IDbConnection para salir del apuro y voy a adelantar que mis entidades admitan una transacción como parámetro de sus métodos 'Guardar'.

Public Overloads Sub Guardar()
        Dim t As New DCE.Datos.Transaccion
        Dim tran As IDbTransaction = t.ComenzarTransaccion
        Dim micn As IDbConnection = tran.Connection
        Try
            Guardar(tran)
            tran.Commit()
        Catch ex As Exception
            tran.Rollback()
            Throw New Exception("SuperEntidad:" & vbCrLf & ex.Message, ex)
        Finally
            micn.Close()
        End Try
    End Sub

    Private Overloads Sub Guardar(ByVal Tran As System.Data.IDbTransaction)
        Me.MiEntidadEmployees.Guardar(Tran)
        Me.MiEntidadJobs.Guardar(Tran)
    End Sub

Capa de Datos

Nos queda por resolver la clase 'transaccion' que se encargará de iniciar la transacción

Public Class Transaccion
    Public Function ComenzarTransaccion() As IDbTransaction
        Try
            Dim Cn As IDbConnection = New SqlClient.SqlConnection
            Cn.ConnectionString = ConnectionSolver.ConectionString
            Cn.Open()
            Return Cn.BeginTransaction(IsolationLevel.Serializable)
        Catch ex As Exception
            Throw New Exception("Transaccion.ComenzarTransaccion:" & vbCrLf & ex.Message, ex)
        End Try
    End Function
End Class

 Y sobreescribir los métodos 'Actualizar' de los componentes de la capa de datos para que admitan la conexión-transacción de la SuperEntidad y se la asigne a los Command de los dataAdapter correspondientes.

    Public Overloads Sub Actualizar(ByVal ds As DataSet)
        Dim t As New Transaccion
        Dim tran As IDbTransaction = t.ComenzarTransaccion
        Dim cn As IDbConnection = tran.Connection
        Try
            Actualizar(ds, tran)
            tran.Commit()
        Catch ex As Exception
            tran.Rollback()
        Finally
            cn.Close()
        End Try
    End Sub

    Public Overloads Sub Actualizar(ByVal ds As DataSet, ByVal tran As System.Data.IDbTransaction)
        SetTran(Me.SqlDataAdapter1, tran)
        Me(ds)
    End Sub

    Private Sub SetTran(ByVal da As SqlClient.SqlDataAdapter, ByVal Tran As SqlClient.SqlTransaction)
        da.UpdateCommand.Connection = Tran.Connection
        da.InsertCommand.Connection = Tran.Connection
        da.DeleteCommand.Connection = Tran.Connection
        da.UpdateCommand.Transaction = Tran
        da.InsertCommand.Transaction = Tran
        da.DeleteCommand.Transaction = Tran
    End Sub

 

Capa de presentación

Et voila. En el formulario agregar las entidades de negocio y la superentidad a la barra de herramientas y arrastrarlas al formulario donde vayan a ser 'bindeadas'.


Si esta información ha sido útil para ti o simplemente de tu interés no olvides valorarlo votando en el frame de la esquina superior derecha de esta página.

Muchas gracias a Jesús López (él sabe por qué).

Tomás Martín


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

System
System.Data
System.Data.XML
System.Data.Drawing
System.Data.Windows.Froms


Fichero con el código de ejemplo: Tomasmm_3capas2.zip - (30) KB


ir al índice principal del Guille