Introducción
Llego el momento de crear una aplicación que haga uso de nuestro servidor creado en la Parte I. En ella implementaremos una serie de métodos que nos permitirá armar y gestionar el array de ClientDataSets.
El Cliente
Empecemos creando un proyecto siguiendo los siguientes pasos:
- 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: NTierXIClient.
La unidad CDSUtil.pas
En ésta parte, debemos de añadir una unidad, en la que implementaremos unos métodos que se encargarán de armar y gestionar el envío del array de ClientDataSets con el Servidor remoto.
- File -> New -> Unit – Delphi for win 32
function RecuperarDeltas(const cdsArray : array of TClientDataset): Variant;
var
i : integer;
LowCDS, HighCDS : integer;
begin
Result:=NULL;
LowCDS:=Low(cdsArray);
HighCDS:=High(cdsArray);
for i:=LowCDS to HighCDS do
cdsArray[i].CheckBrowseMode;
Result:=VarArrayCreate([LowCDS, HighCDS], varVariant);
{Setea el variant con los cambios (o NULL si no hay ninguno)}
for i:=LowCDS to HighCDS do
begin
if cdsArray[i].ChangeCount>0 then
Result[i]:=cdsArray[i].Delta else
Result[i]:=NULL;
end;
end;
{Necesitamos devolver el nombre del Provider y el AppServer desde ésta función.
Utilizaremos ProviderName para llamar AS_ApplyUpdates en la función de CDSApplyUpdates después.}
function RecuperarProviders(const cdsArray : array of TClientDataset): Variant;
var
i: integer;
LowCDS, HighCDS: integer;
begin
Result:=NULL;
LowCDS:=Low(cdsArray);
HighCDS:=High(cdsArray);
Result:=VarArrayCreate([LowCDS, HighCDS], varVariant);
for i:=LowCDS to HighCDS do
Result[i]:=VarArrayOf([cdsArray[i].AppServer, cdsArray[i].ProviderName]);
end;
procedure ReconciliarDeltas(const cdsArray : array of TClientDataset; vDeltaArray: OleVariant);
var
bReconciliar : boolean;
i: integer;
LowCDS, HighCDS : integer;
begin
LowCDS:=Low(cdsArray);
HighCDS:=High(cdsArray);
{Si el paso anterior dió lugar a errores, Reconciliar los datapackets del error.}
bReconciliar:=false;
for i:=LowCDS to HighCDS do
if not VarIsNull(vDeltaArray[i]) then
begin
cdsArray[i].Reconcile(vDeltaArray[i]);
bReconciliar:=true;
break;
end;
{Actualizar los Datasets si es necesario}
if not bReconciliar then
for i:=HighCDS downto LowCDS do
begin
cdsArray[i].Reconcile(vDeltaArray[i]);
cdsArray[i].Refresh;
end;
end;
No olvide agregar en la cláusula uses las siguientes unidades: DbClient, ADODB, SysUtils, Provider, Midas y Variants.
La Interfase del usuario
Llegó el momento de implementar la interfase de usuario, para ello colocaremos en el formulario los siguientes componentes:
2 TDBGrids (DBGridClientes y DBGridTerritorios)
2 TDBNavigators (DBNavigatorClientes y DBNavigatorTerritorios)
2 TClientDataSets (cdsMaster y cdsDetail)
2 TDataSources (dsMaster y dsDetail)
3 TButtons (btnCargarDatos, btnGuardar y btnCancelar)
2 TDBTexts (DBTextEstadoClientes y DBTextEstadoTerritorios)
1 TSocketConnection

Para que nuestra aplicación se conecte remotamente con el servidor de aplicaciones, es necesario modificar algunas propiedades de nuestro componente SocketConnection, como se muestra en la siguiente figura:

En la propiedad ServerName se muestran todos los servidores COM registrados en el servidor remoto,especificado en la propiedad Host. Para activar la conección, modificamos la propiedad Connected a true.
Nuestra aplicación cliente accederá al servidor usando el Borland Socket Server (aunque podría usarse también clientes DCOM o HTTP)
En el evento OnClick del botón btnCargarDatos escribimos el siguiente código:
cdsMaster.Active:= True;
cdsDetail.Active:= True;
Luego implementamos el código en el evento OnClick del botón btnGuardar:
procedure TForm3.btnGuardarClick(Sender: TObject);
var
vDeltaArray: OleVariant;
vProviderArray: OleVariant;
vCDSArray: array[0..1] of TClientDataset;
begin
vCDSArray[0]:=cdsMaster;
vCDSArray[1]:=cdsDetail;
vDeltaArray:=RecuperarDeltas(vCDSArray);
vProviderArray:=RecuperarProviders(vCDSArray);
//Llamamos al nuevo método del servidor, que implementamos en la Parte I.
SocketConnection.AppServer.CDSApplyUpdates(vDeltaArray, vProviderArray, False);
ReconciliarDeltas(vCDSArray, vDeltaArray);
end;
En el evento OnClick del botón btnCancelar escribimos el siguiente código:
cdsMaster.CancelUpdates;
cdsDetail.CancelUpdates;
Por último, compilamos la aplicación y lo echamos a correr, presionamos el botón Cargar datos para obtener los datos desde el servidor remoto, y luego modificamos los datos de las grillas y presionamos el botón
Post del DBNavigator correspondiente. Luego para enviar los cambios al servidor presionamos el botón Guardar, es aquí donde se llama al método implementado en nuestro servidor (no confunda ApplyUpdates con nuestro nuevo método CDSApplyUpdates).

Para controlar los errores de actualización de nuestros ClientDataSets vamos a añadir el código correspondiente en el evento OnReconcileError de nuestro componente cdsMaster:
Action := HandleReconcileError(DataSet, UpdateKind, E);
Hacemos lo mismo para el componente cdsDetail.
Para personalizar y rastrear las modificaciones realizadas en los ClientDataSet implementaremos un procedimiento que nos permita notificar visualmente ésta ocurrencia.
procedure TForm3.UpdateCalcFields(DataSet: TDataSet);
const
UpdateStatusStr: array[TUpdateStatus] of string = ( 'Sin modificar', 'Modificado',
'Insertado', 'Borrado');
begin
with Dataset as TClientDataset do
FieldByName('EstadoActual').Value := UpdateStatusStr[UpdateStatus];
end;
Previamente debimos haber creado un campo calculado llamado ’EstadoActual’, del tipo String y de 20 caracteres de tamaño.
Conclusión
Con ésta segunda parte hemos intentado controlar la atomicidad de transacciones usando ClientDataSets con SQL Server. El código de la aplicación está disponible en la siguiente dirección: http://www.danysoft.com.
Connect with Us