PARTE I. Atomicidad de Transacciones usando un Array de ClientDataSets en Delphi

By: Jose Castillo Reyes

Abstract: Una de las cosas que siempre quise realizar era controlar la actualización en conjunto de varios ClientDataSets que dependieran uno del otro, es decir, que si uno de ellos fallaba, las actualizaciones de los ClientDataSets que ya se habían ejecutado se tendrían que descartar.

    Introducción

De seguro que en algún momento se encontró en la necesidad de controlar la atomicidad en la actualización de varios ClientDataSets, es decir, si un ClienDataSet falla el motor de base de datos debe de descartar las actualizaciones de los ClientDataSets anteriormente ejecutados.

    Array de ClientDataSets

Claro, para ubicarnos en el contexto anterior, el servidor de aplicaciones debería, primero, permitirnos pasarle como parámetro un array conformado de varios ClientDataSets, segundo, debería de existir un método que se encargue de llevar a cabo la actualización en la base de datos. Para el desarrollo de los ejemplos usaremos la base de datos Northwind que se encuentra en el SQL Server 2000 al cual accederemos usando los componentes dbGo de Delphi.

Bien, en éste primer artículo seguiremos paso a paso la creación del Servidor DataSnap que implemente el método requerido.

  • File->New->VCL Forms Applications – Delphi for win 32
  • Save All

El asistente solicitará el nombre de la unidad al cual la llamaremos: uMain.pas.

Luego, asignamos el nombre del proyecto: NTierXIServer.

    El Servidor de Aplicaciones

Hasta aquí ya tenemos un proyecto creado que además cuenta con un formulario principal. Como nuestro servidor de aplicaciones es la que llevará la parte dura, es en ella que debemos de implementar el código necesario para gestionar y controlar la atomicidad de las actualizaciones.

Para empezar, será necesario agregar un Remote Data Module, que en realidad es un COM Automation Server (Servidor de Automatización COM) el que será accesado remotamente por los clientes usando DCOM, HTTP, o Sockets.

  • File -> New -> Other -> Multitier -> Remote Data Module

Delphi nos mostrará el asistente solicitando el nombre de nuestro Remote Data Module (También servirá de nombre base para la interfase de la clase ). En el campo CoClass Name asignaremos el nombre de coRDM, como se muestra en el gráfico siguiente:

Hide image
coRDM

Presionamos el botón OK y guardamos la unidad como uRDM.pas. Delphi ha generado y añadido al proyecto un archivo llamado NTierXIServer_TLB.pas. Sobre el Remote Data Module, colocaremos los siguientes componentes de acceso a datos, como se muestra en el siguiente grafico.

Hide image

La descripción detallada de los componentes dbGo que usamos se muestra a continuación.

1 TADOConnection (MDCDSDB),

2 TADOQuery (qryMaster y qryDetail),

2 TDataSetProvider (prvMaster y prvDetail),

TADOConnection, usa la base de datos Northwind del SQL 2000. Al crear nuestra cadena de conección a la base de datos a través del asistente, nos queda algo así:

Provider=SQLOLEDB.1; Password=xxxx; Persist Security Info=True; User ID=sa; Initial Catalog=Northwind; Data Source=jcastillo\sql2000;Use Procedure for Prepare=1; Auto Translate=True; Packet Size=4096; Workstation ID=JCASTILLO

Dependiendo del entorno de trabajo de su red, la cadena de conección no deberá ser necesariamente igual a ésta.

En los componentes TADOQuery, debemos especificar las sentencias SQL que obtendrán los datos de los empleados. Por eso, en la propiedad SQL del qryMaster, escribimos la siguiente sentencia:

select * from employees

Luego, en la propiedad SQL del qryDetail, especificamos la sentencia SQL que obtendrá los datos de los territorios que corresponden a los empleados de la empresa:

select * from EmployeeTerritories

where employeeID=:employeeID

Conectamos el componente prvMaster con el qrymaster a través de la propiedad DataSet. Hacemos lo mismo con los componentes prvDetail y qryDetail.

    El método CDSapplyUpdates y el Type Library

Llegó el momento de implementar nuestro método personalizado que gestione las actualizaciones directamente a la base de datos. Para eso, vamos a usar una utilidad que viene en Delphi desde hace mucho tiempo, el Type Library (Librería de Tipos).

El Type Library genera archivos binarios del tipo .tlb. A través de ésta herramienta podremos incluir información acerca de tipos, objetos e interfaces expuestas por nuestra aplicación servidor. La información de los objetos en la librería estarán disponibles para nuestra aplicación cliente que construyamos después. Para abrir el Type Library siga los siguientes pasos:

View -> Type Library

En nuestro caso Type Library, nos permitirá declarar el método CDSapplyUpdates para que ésta se encuentre disponible a través de la Interfase del IAppServer.

Seleccionamos la Interfase IcoRDM, y escogemos el icono New Method desde la barra de herramientas. Ahora el método miembro es añadido al panel de la lista de objetos, solicitando añadir un nombre.

Escribimos el nombre para el método miembro, como se muestra en la figura:

Hide image

Luego, en la ficha Parameters, creamos los siguientes parametros: vDeltaArray, vProviderArray y Local.

Hide image

Para implementar los cambios en la interfase, escojemos el ícono Refresh Implementation, y Delphi generará el siguiente código en la ficha Text:

[

Id(0x0000012D)

]

HRESULT _stdcall CDSapplyUpdates([in] VARIANT * vDeltaArray, [in] VARIANT * vProviderArray, [in] VARIANT_BOOL Local);

Ahora, solo nos queda localizar el nuevo método dentro del código fuente de la unidad del Remote Data Module, si se encuentra en el diseñador, presione F12 para ir al área de código. Aquí completaremos el cuerpo del método con el código necesario para terminar con la implementación.

procedure TcoRDM.CDSapplyUpdates(var vDeltaArray, vProviderArray: OleVariant;

Local: WordBool);

var

i : integer;

LowArr, HighArr: integer;

P: PArrayData;

begin

MDCDSDB.Connected:=true;

MDCDSDB.BeginTrans;

try

LowArr:=VarArrayLowBound(vDeltaArray,1);

HighArr:=VarArrayHighBound(vDeltaArray,1);

P:=VarArrayLock(vDeltaArray);

try

for i:=LowArr to HighArr do

AplicarDelta(vProviderArray[i], P^[i], Local);

finally

VarArrayUnlock(vDeltaArray);

end;

MDCDSDB.CommitTrans;

except

MDCDSDB.RollbackTrans;

end; 

end;

Como puede observar, BeginTrans indica el inicio de una transacción explícita, y envuelve las actualizaciones de los ClientDataSets en una transacción. Si alguno de ellos resulta en error, se disparará una excepción, que realizará un Rollback en la transacción; en caso contrario, si todo sale como esperamos se confirma la transacción mediante el CommitTrans.

Observe que llamamos a un procedimiento llamado AplicarDelta. AplicarDelta no ha sido declarada en el Type Library, debido a que no es necesario que las aplicaciones clientes accedan directamente a ella. Al retornar, Delta contendrá un data packet que contiene todos los registros que no se pudieron aplicar a la base de datos. Delphi necesita el nombre del provider, y éste es pasado en el elemento 1 de la variant AProvider.

procedure AplicarDelta(AProvider: OleVariant; var Delta : OleVariant; Local: Boolean);

var

ErrCount : integer;

OwnerData: OleVariant;

begin

if not VarIsNull(Delta) then

begin

if Local then

Delta := (IDispatch(AProvider[0]) as IAppServer).AS_ApplyUpdates(AProvider[1], Delta, 0, ErrCount, OwnerData)

else

Delta := IAppServerDisp(IDispatch(AProvider[0])).AS_ApplyUpdates(AProvider[1], Delta, 0, ErrCount, OwnerData);

if ErrCount > 0 then

SysUtils.Abort; // Esto causa un Rollback

end;

end;

Ahora, sólo nos queda compilar la aplicación y echarlo a correr, si todo nos fue bien, tendremos una pantalla como la siguiente:

Hide image

Una novedad que debemos indicar es que al crear el Remote Data Module, éste aparece como Available forms (formularios disponibles) y para poder usarlo debemos de crearlo explícitamente mediante el método Create(). Para eso, en el evento Create de nuestro formulario principal del proyecto escribimos el siguiente código:

procedure TfMain.FormCreate(Sender: TObject);

begin

TcoRDM.Create(Self);

end;

También, debemos de resaltar que en Delphi 2007, los servidores COM del tipo out-of-process, como el que acabamos de implementar, ya no se registran en el sistema automáticamente cuando echamos a correr la aplicación. CodeGear ha visto por conveniente, dejarle esta tarea al desarrollador, no se olvide. Una manera de registrar nuestro servidor es añadir el parámetro /regserver en el cuadro de dialogo:

  • Run -> Parameters

Como se muestra en la figura de abajo.

Hide image

Finalmente, presionamos F9 para ejecutar la aplicación, que, con la especificación realizada, hará que se registre nuestro Servidor.

    Conclusión

En ésta primera parte hemos demostrado una de las formas que nos permiten realizar y controlar la atomicidad en las transacciones usando ClientDataSets con SQL Server, también, hemos visto algunas novedades que nos trae Delphi 2007. En la segunda parte, nos enfocaremos en el desarrollo del Cliente.

Server Response from: ETNASC03