Programación en 3 capas (II)Transacciones
Fecha: 18/Oct/2005 (17/10/05)
|
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 SubEl 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 SubCapa 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 ClassY 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