PDA

Ver la Versión Completa : adhoc actualizacion de la cache de clientdataset


juniorSoft
15-11-2007, 17:55:47
saludos

a continuacion le describire mi problema tal vez algunos de ustedes han podido resolver tal situacion, mostrare solo las relaciones entre las tablas que
ejemplifican el problema

tengo las tablas

ventas

detventas

detallesdepago

ingefectivo

ingtarjeta

ingcheque


todo me funcionara muy bien si solo fueran las tablas ventas y detventas
tengo todo configurado en el servidor de capa intermedia para estas tablas
pero lo que me planteo hacer es que al momento de el cliente realizar el pago
halla un grid conectado a la tabla detallesdepago el cual se puedan hacer diferentes tipos de pagos para completar el total de la factura mediante una ventana que se despliege y se vallan eligiendo los tipos de pago e introduciendo sus montos hasta completar el total de la factura. introduciendo datos en las tablas de ingefectivo, ingtarjeta o ingcheque
y que se valla actualizando la tabla detallesdepago tambien en la cache
hasta que se le de al boton guardar


entoces los campos que interesan son los siguientes de estas tablas

ventas
idventa
.
.

detventas
iddetventa
idventa
.
.

detallesdepago
iddetpago
idtipoentidad
identidad
.
.
.

ingefectivo
idingefectivo
iddetpago
.
.
y las tablas
ingtarjeta y ingcheque con los campos parecidos a ingefectivo mas sus campos especificos como portador, notarjeta etc.

entonces detallesdepago se relaciona con ventas por el campo identidad
el campo idtipoentidad me identifica el tipo de relacion a la que se refiere en este caso es ventas, lo hago asi por que hay mas tablas relacionadas con detallesdepago y no quiero crear campos nulos para diferentes tipos de situaciones

entonces lo que quiero hacer es un metodo remoto que sea el encargado de actualizar los datos que tienen los clientdataset en su delta, haciendo la relacion Master detail en el modulo cliente asi podria aprovechar los componentes sql que estan en el servidor de capa intermedia para las restantes situaciones faltantes


se que tendre que ocuparme de poner el codigo dentro de una transacion
manipular los errores que se puedan genera entre otros tal vez algunos de ustedes tienen algun ejemplo parecido que me pueda servir de ayuda

espero no haber aburrido mucho con tantas palablas para explicar lo que quiero y que ademas me hayan captado la idea, pero muchas gracias por su atencion.

Al González
15-11-2007, 19:51:53
Te invito a que nos plantees el problema de la manera más simplificada posible (sin perder claridad). Digamos, con dos o tres tablas solamente, y dándonos un "ejemplo de pizarrón" para comprender mejor lo que buscas.

Gracias.

Al González. :)

juniorSoft
16-11-2007, 18:18:22
he estado buscando documentacion en varios sitios

y me ha llegado una idea


para simplificar mas la explicacion de lo que quiero hacer

es solo crear un metodo remoto en la capa intermedia que sea el encargado de mandar las actualizaciones al servidor sql del contenido de la cache de los clientdataset en vez de utilizar el metodo applyupdate en la capa cliente de dicho componente ya que hay demasiadas relaciones complejas entre tablas. y queria envolverlas todas en una transaccion. Sé que el componente provider envuelve en una transaccion los datos que se envian del clientdataset al ejecutar el metodo applyupdate pero eso es solo si todas las tablas estan relacionadas en la capa intermedia.

para que me comprendan mejor

supongamos que tengo una funcion llamada serv que ya me devuelve el servidor de capa intermedia para poder ejecutar los metodos remotos entonces seria

serv.actualizardatos(clientdataset1.data, clientdataset2.data... )

y que sea ese metodo el que se encargue de la actualizcion

y muchas gracias por su interes...

Al González
16-11-2007, 19:24:33
Hola Juniorsoft, creo que ya entendí el punto.

Me he topado con la misma inquietud y por ello hace poco terminé la versión alfa de un derivado de TClientDataSet con el que puedes hacer un ApplyUpdates "transaction-aware" (para esas ocaciones en que uno quiere manejar varios conjuntos de datos en una sóla transacción).

Pero analicemos el problema por la vía de una solución totalmente nativa, considerando este seudocódigo:


StartTransaction

dt1.ApplyUpdates
dt2.ApplyUpdates
dt3.ApplyUpdates

If todo bien Then
Commit
Else
Rollback


Dinos, ¿cuál es el problema concretísimo que te surgiría con esta organización de instrucciones?

juniorSoft
16-11-2007, 20:24:53
ok

cuando yo tengo clientdataset anidados maestros- detalles relacionados en el modulo de datos remoto es facil manejar la transaccion de varios dataset ya que el clientdataset maestro me trae los campos de tipo dataset que se relacionaron en los componentes sql del modulo remoto y la transaccion por defecto del provider los envuelve a todos e incluso puedo llamar storeprocedure dentro de los eventos del provider y estos quedan envueltos tambien en la transaccion.

Pero lo que quiero hacer es un formulario pequeño que me maneje los tipos pagos ya sea en efectivo, cheque o tarjeta y que este formulario me sirva no solo para las ventas sino que tambien me sirva para otros mantenimientos como por ejemplo cuentas por cobrar sin tener que hacer nuevas relaciones de las mismas tablas para el mismo proposito, aclarando que para hacer un pago se pueden utilizar los tres tipos de pago para completar el total a pagar y es donde entra en juego la tabla detallesdepago que tiene una relacion uno a uno con estos tipos de pago. si hago la relacion directa en el modulo de datos remoto por ejemplo con ventas, ya no podre utilizar esos mismos componentes sql para utilizarlos en la relacion con cuentas por cobrar.

si se pueden separar los applyupdates uno para la factura en si y otro para los pagos pero que queden envueltos los dos en una misma transaccion como el ejemplo que escribiste

si has chequeado en la cara oculta de delphi 4 en el capitulo de midas su autor escribio un ejemplo para delphi 3 de un metodo remoto que es el encargado de actualizar los datos para envolver en una transaccion por que los clientdataset tienen envuelta la transaccion a partir de delphi 4

gracias por tu ayuda...:)

Al González
17-11-2007, 10:09:54
Hola de nuevo.

...si se pueden separar los applyupdates uno para la factura en si y otro para los pagos pero que queden envueltos los dos en una misma transaccion como el ejemplo que escribiste...
Bueno, estrictamente hablando, las "aplicaciones al servidor" de los dos conjuntos de datos sí quedan envueltas en la misma transacción aunque ellos no estén relacionados entre sí.

La duda que tengo sobre tu planteamiento es:

1. Si no sabes que desde tu aplicación cliente puedes controlar el inicio y cierre de la transacción, evitando que eso lo haga de manera implícita un componente proveedor, y con ello poder envolver en una transacción varios conjuntos de datos no relacionados entre sí (aunque esto depende del modo en que se configure el servidor de aplicaciones).

2. Si, conociendo esa capacidad, te preocupa entonces lo que pasa con la memoria de los conjuntos de datos que sí lograron aplicar los cambios de manera exitosa, cuando la transacción tuvo que ser revertida (rollback) debido a un subsiguiente conjunto de datos que no logró hacer sin errores su ApplyUpdates.

Todo lo que has planteado está muy completo, y ayuda a entender muchas cosas, pero sigue quedando un poquitín abstracto. Tomemos dos conjuntos de datos clientes llamados dt1 y dt2, los cuales están enlazados a diferentes proveedores de la capa intermedia (no están relacionados como maestro-detalle).

¿Qué problema te representa que lo hagas de esta manera?


Serv.IniciarTransaccion;

Try
dt1.Append;
dt1.FieldByName ('A').Value := 1;
dt1.Post;

dt2.Append;
dt2.FieldByName ('A').Value := 1;
dt2.Post;

If (dt1.ApplyUpdates (0) = 0) And (dt2.ApplyUpdates (0) = 0) Then
Serv.CometerTransaccion
Else
Serv.RevertirTransaccion;
Except
If Serv.EnTransaccion Then
Serv.RevertirTransaccion;

Raise;
End;


Yo veo uno. El de que si falla dt2.ApplyUpdates y se revierte (rollback) la transacción, podrás revertir también la memoria de dt2 (con su método CancelUpdates o su propiedad SavePoint). Pero a dt1 ya no lo podrás regresar a su estado previo debido a que ya limpió el "ChangeLog" por no haber tenido errores su respectivo ApplyUpdates. Solución típica y nada eficiente: refrescar dt1. Y esa es una de las razones por las cuales he estado trabajando en el TMagiaClientDataSet (un derivado con ciertas capacidades transaccionales).

Pero no estoy seguro sí esto te preocupa a ti también, o es otra cosa de menor, igual o mayor importancia. Esperamos tus comentarios.

Un abrazo que no se revierte.

Al González. :)

juniorSoft
19-11-2007, 15:27:28
Gracias AL Gonzalez

Yo creo que andamos por el mismo camino y eso es una de las cosas q mas me preocupa cuando tengo que hacer applyUpdate a conjuntos de datos que estan conectados a diferentes provider y que deben guardar los datos en una misma transaccion. Entoces ese componente q estas desarrollando si esta por donde yo estoy pensando debe conectarse con varios provider a la vez y cuando se le de applyupdate al componente, este los envuelva todos en una misma transaccion y si se produce cualquier error se queda el logchange como antes de darle applyupdate. agradezco tus respuestas ya que nos vamos aclarando en un problema comun que le sucede a muchos de nosotros.

Al González
19-11-2007, 20:34:41
Grosso modo, mi solución tiene que ver con un sistema de puntos de restauración (save points) universales encadenables, implementados en clases derivadas de TSQLConnection y TClientDataSet. Cuando uno de estos conjuntos de datos cliente hace ApplyUpdates, mira si hay un punto padre "marcado" (transacción iniciada en el servidor), en cuyo caso, efectivamente, conserva el ChangeLog tras la operación. Pero debe realizarse el ApplyUpdates por cada conjunto de datos, como estamos acostumbrados. Cuando ese punto padre se confirma (Commit de transacción), todos esos conjuntos de datos "hijos" de la transacción y pendientes de confirmar hacen su MergeChangeLog .

Pero para no ir tan lejos, te comento que la clave para conservar el ChangeLog después de un ApplyUpdates es evitar que se ejecute el método TCustomClientDataSet.MergeChangeLog cuando realizas esa operación.

En el código fuente de DBClient.pas podrás observar que ApplyUpdates llama al método Reconcile, y éste a MergeChangeLog cuando el envío al servidor no tuvo errores. Como Reconcile y MergeChangeLog no son métodos virtuales, no pueden ser redefinidos (Override) en una clase derivada. Entonces, si se quiere evitar que un ApplyUpdates termine llamando a MergeChangeLog, toca redefinir el propio método ApplyUpdates, el cual sí es virtual.

Puedes reescribir el código de dicho método en una clase derivada "TMiClientDataSet", evitando que se llame a Reconcile cuando DoApplyUpdates (otro método involucrado ahí) no regrese errores. De esa manera consigues que el registro de cambios (ChangeLog) permanezca intacto aún después de aplicar todas las actualizaciones al servidor.

Más adelante, cuando hagas el Commit o el Rollback de la transacción, podrás llamar directamente a MergeChangeLog o a CancelUpdates, respectivamente. De esta forma logras envolver a cualquier grupo de objetos TClientDataSet en una transacción.

Espero te sea de utilidad. Síguenos contando sobre tus progresos en el caso.

Un abrazo virtual.

Al González. :)

juniorSoft
19-11-2007, 21:16:53
ok gracias por tu respuesta,

entoces los pasos son
Redefinir el metodo applyupdate para hacer una condicion preguntando por el metodo doapplyupdate que de seguro es una funcion si este metodo es mayor a cero entonces que ejecute el metodo concile

se podria hacer una clase interpuesta de la clase tclientdataset

Tclientdataset = class(dbclient.Tclientdataset)
procedure applyupdate(...);override;

seria algo como
Tclientdataset.applyupdate(....);
begin
if doapplyupdate(...) > 0 then
self.concile(..);
.
.
.
end;

a ver si estoy captando tu idea, por favor me corriges si estoy perdido en algo..

Al González
19-11-2007, 22:26:23
¡Hola!

¿Ya echaste un ojo a los métodos que mencioné? Empecemos por revisar el código fuente de ApplyUpdates y Reconcile de Delphi 7 (quizá sea igual o muy parecido en versiones cercanas):


function TCustomClientDataSet.ApplyUpdates(MaxErrors: Integer): Integer;
var
RootDataset: TCustomClientDataset;
begin
CheckBrowseMode;
RootDataset := Self;
while RootDataset.FParentDataSet <> nil do
RootDataset := RootDataset.FParentDataset;
with RootDataset do
if ChangeCount = 0 then
Result := 0 else
Reconcile(DoApplyUpdates(Delta, MaxErrors, Result)); { <--- Esta llamada a Reconcile es la que debemos condicionar
(ojo: DoApplyUpdates debe ser llamado siempre) }
end;



function TCustomClientDataSet.Reconcile(const Results: OleVariant): Boolean;
var
RCB: Pointer;
I: Integer;
AField: TField;
begin
if VarIsNull(Results) then MergeChangeLog else
begin
...
end;
Result := (ChangeCount = 0);
end;


Efectivamente, DoApplyUpdates es un método función:

function TCustomClientDataSet.DoApplyUpdates(Delta: OleVariant; MaxErrors: Integer;
out ErrorCount: Integer): OleVariant;

Pero su dato de resultado no es un entero que indica la cantidad de errores (eso lo regresa en el parámetro por variable ErrorCount), sino un variante arreglo conteniendo información detallada sobre los errores ocurridos. Cuando ese resultado es Null, significa que no ocurrieron errores al enviar los cambios al servidor.

ApplyUpdates llama directamente a Reconcile dándole dicho variante como parámetro:

Reconcile(DoApplyUpdates(Delta, MaxErrors, Result));


Cuando ese parámetro es Null, lo único que hace Reconcile es llamar a MergeChangeLog:

if VarIsNull(Results) then MergeChangeLog else

De ahí que, sin ninguna preocupación, podemos evitar la llamada al método Reconcilie cuando no haya errores, colocando, efectivamente, una condición dentro del nuevo ApplyUpdates.

¿Vamos captando la idea?

Por aquí seguimos.

Al González. :)

juniorSoft
20-11-2007, 15:47:27
saludos

Estube chequeando ayer en la referida unidad dbclient.pas el metodo appyupdates hasta ahora voy captando tus consejos y veo que vamos por la solución. Tengo otra pequeña duda ya de menor importancia y es que si hacemos varios applyupdate en la capa cliente estaremos haciendo varias llamadas remotas al servidor de aplicaciones como escribí en el titulo adhoc quiere decir que todos los paquetes se envien en una sola llamada remota y asi tratar de reducir el trafico por la red aunque creo que hacer los applyupdate en la capa cliente no debe relentilizar mucho al guardar los datos. Pero seguimos donde estabamos que es lo que mas me interesa por ahora ya hecha la condicion que evita llamar a reconcile si no ocurrierron errores. Que nos hace falta?

Al González
20-11-2007, 19:27:05
¡Hola a todos!

...otra pequeña duda ya de menor importancia y es que si hacemos varios applyupdate en la capa cliente estaremos haciendo varias llamadas remotas al servidor de aplicaciones...aunque creo que hacer los applyupdate en la capa cliente no debe relentilizar mucho...
No te preocupes por eso. Al estar todos los ApplyUpdates envueltos en una misma transacción, no hay diferencias significativas de velocidad.

...ya hecha la condicion que evita llamar a reconcile si no ocurrierron errores. Que nos hace falta?
Entre otras cosas, que nos muestres tu nuevo ApplyUpdates.

Seguimos en contacto.

Al González. :)

juniorSoft
20-11-2007, 20:29:09
ok aqui esta el metodo AppyUpdates modificado

function TCustomClientDataSet.ApplyUpdates(MaxErrors: Integer): Integer;
var
RootDataset: TCustomClientDataset;
Aux:OleVariant;
begin
CheckBrowseMode;
RootDataset := Self;
while RootDataset.FParentDataSet <> nil do
RootDataset := RootDataset.FParentDataset;
with RootDataset do
if ChangeCount = 0 then
Result := 0 else
begin
Aux:=DoApplyUpdates(Delta, MaxErrors, Result);
if not VarIsNull(Aux) then
Reconcile(Aux);
end;
end;

Al González
20-11-2007, 22:02:46
¡Hola!

Como seudo código está muy bien.

Tu nueva clase no debe llamarse TCustomClientDataSet, ese nombre pertenece a la clase padre de TClientDataSet. Debes nombrarla de otra manera.

Ese código te dará problemas al compilar porque FParentDataset es un campo privado, pero no te preocupes, puedes acceder al mismo objeto mediante la propiedad DataSetField.DataSet.

Comienza a construir tu clase, compila e infórmanos de tus avances.

Saludos.

Al González. :)

juniorSoft
20-11-2007, 23:46:43
gracias...

ok como no tenia un delphi al momento de postear lo mostre asi


pero en realidad estoy interesado en hacer una clase interpuesta y no un componente ya que tendria entoces que instalarlo cada vez que tenga q instalar delphi aunque no se si haya problemas con esto.

lo que hago es crear una clase llamada

TClientdataset = class(Dbclient.TClientdataset)
function applyupdates(MaxErrors:integer):integer;override;
.
.
.
end;

Al González
21-11-2007, 00:16:53
De acuerdo. Me parece que también funcionaría con una clase interpuesta. Adelante. No dejes de contarnos sobre tus avances.

Saludos.

Al González. :)

juniorSoft
21-11-2007, 23:44:26
saludos Al Gonzalez.

ok ahora me queda la parte de que se llame a MergeChangeLog automaticamente de todos los clientdataset afectados al darle commit a la transaccion por que sino cada vez que le de commit tendre que llamar ese metodo para cada uno de los clientdataset a los que les de applyupdates. Aunque ya la parte mas importante era de encapsular en una transaccion los clientdataset que se les de applyupdates y que estos no pierdan los valores del delta hasta que se le de commit.

:)

juniorSoft
27-12-2007, 15:34:18
Hola AL Gonzalez,

Creo que por estar buscando mas simplicidad me he complicado Más haciendo una clase interpuesta por que al final no tengo idea de como llamar al metodo mergechangelog o cancelupdates despues de aplicada la transaccion. dicho de otro modo, si puedo hacerlo pero no quiero hacerlo cada vez que tenga que hacer una ventana que se presente la misma situación. Me he decido por hacer el componente pero tengo la misma duda al respecto de en que momento llamar los referidos metodos despues que se aplique la transaccion.

:rolleyes:

Al González
27-12-2007, 16:29:00
¡Hola de nuevo!

...llamar al metodo mergechangelog o cancelupdates despues de aplicada la transaccion. dicho de otro modo, si puedo hacerlo pero no quiero hacerlo cada vez que tenga que hacer una ventana que se presente la misma situación. Me he decido por hacer el componente pero tengo la misma duda al respecto de en que momento llamar los referidos metodos despues que se aplique la transaccion...
Hay varias soluciones para esto. En lo personal empleo un sistema de objetos "save points" subordinados que reaccionan en cadena, pero no creo que sea necesario aplicar algo tan complejo en este caso.

En realidad no es demasiado el esfuerzo de escribir unos cuantos "dtXXX.MergeChangeLog; / dtXXX.CancelUpdates;" por forma, dado el beneficio que obtienes con la nueva implementación de ApplyUpdates, pero comprendo tu punto de vista bibliotecario: siempre puede hacerse más.

Como lo veo, tienes dos opciones relativamente fáciles de implementar:

1. Diseñar tus formas de captura bajo un esquema de herencia visual, colocando en tu "forma transaccional base" el código que será común para todas las formas de captura, incluyendo esto de las llamadas a MergeChangeLog y CancelUpdates (los "Commit / Rollback" del lado cliente). En ello podrás emplear bucles que recorran todos los conjuntos de datos usados por la forma (considerando que por cada uno tuvieses un TDataSource dentro de la misma).

2. Añadir un poquito más de código a tu nuevo método ApplyUpdates, haciendo que el conjunto de datos en cuestión (Self) se agregue a una lista global llamada "TransDataSets" o algo por el estilo. Cada vez que quieras cometer o revertir todo el grupo de conjuntos de datos involucrados en una transacción, sólo tendrías que recorrer dicha lista y llamar al método MergeChangeLog / CancelUpdates por cada elemento de la misma.

Te recomendaría más la opción #2. Y considerando que estás dispuesto a hacer de tu nueva clase un componente derivado (en lugar de una clase interpuesta), lo cual no es mala idea dada la importancia de la nueva funcionalidad, creo que, con el propósito antes mencionado, convendría crear también una clase TTransClientDataSets, derivada de TList o de TObjectList, definiéndole dos métodos para hacer el MergeChangeLog / CancelUpdates sobre todos los conjuntos de datos contenidos.

Espero te sea de utilidad y sirva de orientación. Te invito a publicar el código final de tus nuevas clases, para compartir en su totalidad esta interesante y muy útil experiencia orientada a objetos con nuestros compañeros.

Seguimos en contacto.

Al González. :)

canelita
13-01-2008, 04:34:53
Cordial saludo.

Me gustaria una explicacion profunda del componente Tlientdataset

Gracias

¡Hola de nuevo!


Hay varias soluciones para esto. En lo personal empleo un sistema de objetos "save points" subordinados que reaccionan en cadena, pero no creo que sea necesario aplicar algo tan complejo en este caso.

En realidad no es demasiado el esfuerzo de escribir unos cuantos "dtXXX.MergeChangeLog; / dtXXX.CancelUpdates;" por forma, dado el beneficio que obtienes con la nueva implementación de ApplyUpdates, pero comprendo tu punto de vista bibliotecario: siempre puede hacerse más.

Como lo veo, tienes dos opciones relativamente fáciles de implementar:

1. Diseñar tus formas de captura bajo un esquema de herencia visual, colocando en tu "forma transaccional base" el código que será común para todas las formas de captura, incluyendo esto de las llamadas a MergeChangeLog y CancelUpdates (los "Commit / Rollback" del lado cliente). En ello podrás emplear bucles que recorran todos los conjuntos de datos usados por la forma (considerando que por cada uno tuvieses un TDataSource dentro de la misma).

2. Añadir un poquito más de código a tu nuevo método ApplyUpdates, haciendo que el conjunto de datos en cuestión (Self) se agregue a una lista global llamada "TransDataSets" o algo por el estilo. Cada vez que quieras cometer o revertir todo el grupo de conjuntos de datos involucrados en una transacción, sólo tendrías que recorrer dicha lista y llamar al método MergeChangeLog / CancelUpdates por cada elemento de la misma.

Te recomendaría más la opción #2. Y considerando que estás dispuesto a hacer de tu nueva clase un componente derivado (en lugar de una clase interpuesta), lo cual no es mala idea dada la importancia de la nueva funcionalidad, creo que, con el propósito antes mencionado, convendría crear también una clase TTransClientDataSets, derivada de TList o de TObjectList, definiéndole dos métodos para hacer el MergeChangeLog / CancelUpdates sobre todos los conjuntos de datos contenidos.

Espero te sea de utilidad y sirva de orientación. Te invito a publicar el código final de tus nuevas clases, para compartir en su totalidad esta interesante y muy útil experiencia orientada a objetos con nuestros compañeros.

Seguimos en contacto.

Al González. :)

Al González
04-11-2012, 09:41:58
Hola Maniche.

Hoy matizaría un montón de cosas de aquellos mensajes míos, pero en esencia fue y sigue siendo posible hacer que varios conjuntos de datos clientes conserven su "bitácora de cambios", aun después de aplicar éstos al servidor de forma exitosa (lo normal o "nativo" es que una vez aplicados se borre esa bitácora).

Lo que le planteaba a juniorSoft (a quien le mando un saludo) funciona con DataSnap, pues TClientDataSet, MIDAS, forma parte de DataSnap (si bien el puro componente TClientDataSet sirve para más cosas).

Pero en esto de las bases de datos hay cierta buena práctica que se valora con el tiempo y a la cual uno debe intentar orientarse (aunque a veces cuesta trabajo o no gusta): Que el inicio y el cierre de una transacción de base de datos no dependan de diferentes órdenes o envíos de la aplicación cliente, sino que una sola orden del cliente sea el desencadenante tanto para abrir la transacción, como para grabar los cambios y cerrar la transacción. Esto es una de las diversas razones para tener una "capa intermedia", un programa normalmente sin interfaz de usuario llamado servidor de aplicaciones que, instalado en una computadora respetable y poco propenso a mal funcionamiento, sirve de apoyo al motor de bases de datos, programa al que sí le damos el derecho de abrir y cerrar transacciones abiertamente.

DataSnap facilita enviar "todo en un paquete", para que se cumpla el que sea una sola orden del lado cliente la desencadenante de toda la transacción, esto cuando se usan conjuntos de datos "anidados" (nested data sets), pero hay que subrayar que ese esquema está pensado para los casos donde todos los conjuntos de datos de la transacción están organizados en un árbol de relaciones maestro-detalle (a no ser que se quiera abrir y cerrar la transacción explícitamente desde el cliente).

Mi necesidad de que pudiera aplicarse un esquema "transaccional" a varios conjuntos de datos que no necesariamente estuviesen relacionados entre sí, fue lo que me llevó a estudiar y redefinir los métodos de TClientDataSet que menciono en los mensajes anteriores del hilo. Pero si bien soluciona esa necesidad de manejar cambios de tablas diversas en una sola transacción, pierde una pieza importante: que el servidor de aplicaciones evite abrir la transacción (y por tanto no empiece a guardar datos) antes de que el lado cliente termine de enviarle todos los cambios de todas las tablas involucradas.

Cómo recuperar esa pieza que se pierde por no usar conjuntos de datos anidados es algo que me interesa estudiar, y creo que todas las ideas que se tengan son bienvenidas (se me ocurren cosas como usar el OwnerData de un único ApplyUpdates, o dotar a los TDataSetProvider con la capacidad de "retener" los cambios) . :)

Por lo pronto, Maniche, si fueras tan amable de exponer con detalle y claridad tu interés en el tema para ver qué solución podría ser la más adecuada...

Saludos.

P.D. canelita, perdona por no responder en su momento. TClientDataSet tiene bastantes cosas sobre las que se puede profundizar. Aquí en el foro hay varios hilos que tratan sobre esa clase y no se diga la cantidad de páginas Web que explican algunos de sus más útiles aspectos. Si lees esto nos dirás si todavía buscas documentación o en el mejor de los casos nos compartirás algunos enlaces hacia lo que hayas encontrado. :)

Casimiro Notevi
04-11-2012, 11:24:53
Hola, por error he borrado el mensaje de Maniche, que es a quien ha contestado Al González :o:o:o
A ver si se puede rescatar...
Disculpas.
Saludos.

Casimiro Notevi
04-11-2012, 11:26:13
Para quien le interese, aquí (http://intitec.com/varios/La_potencia_de_los_TClientDataSet.pdf) hay un estupendo tutorial de TClientDataSet.

Al González
04-11-2012, 20:45:22
Hola, por error he borrado el mensaje de Maniche...
No pasa nada, Casi, somos humanos. :)

Los domingos no es uno de los tres días de la semana en que reviso mi buzón de correo electrónico, pero hoy hice una excepción para recuperar de ahí la notificación de Club Delphi que me llegó con el mensaje borrado del usuario Maniche. Lo pongo íntegro:

Holas "Al González" & "juniorSoft"
Muy buenos los comentarios y gracias de verdad por aclarar varios conceptos que ayudan a los amigos de ClubDelphi.

Siguiendo con el tema un poco: juniorSoft nos tienes pendiente que puedas publicar para los amigos como te quedo tu código.

Amigo AL GONZALEZ... quería hacerte una pregunta, si todo lo explicado en el tema lo puedo aplicar en DATASNAP con Delphi XE3 si tuvieras un ejemplo que nos puedas compartir.

La idea principal que he captado es:
*HACER MÉTODOS TANTO EN EL CLIENTE y/o SERVIDOR QUE PERMITAN ACTUALIZAR UN GRUPO DE CLIENTDATASET(DELTAS) HACIA EL SERVIDOR Y A LA VEZ HACER QUE ESTOS DELTAS ACTUALIZADOS SE ACTUALIZEN EN EL CLIENTE. USANDO EL METODO "MERGECHANGELOG".*

Se ve muy bien y sería muy bueno poder hacerlo en datasnap ya que eso evitaría estar haciendo applyupdate por cada dataset ó tendríamos que usar el RECONCILE.

Por favor si tienen un ejemplo compartirlo.

Saludos y muchas gracias. ^\||/

No creo que sea necesario tomarse la molestia de reinsertarlo como mensaje único, pero ya Maniche se pronunciará. :)

Saludos y a dormir tranquilo esta noche. ;)

Casimiro Notevi
04-11-2012, 21:17:32
Saludos y a dormir tranquilo esta noche. ;)
¡¡¡Gracias!!! :)

Maniches
06-11-2012, 00:13:10
Hola Amigos de ClubDelphi, Gracias Al GonzaleZ por responder a mi pregunta, Disculpas por no responder antes estuve con un refriado muy fuerte.

Bueno he preparado un ejemplo que voy a adjuntar para todos los interesados. Ya que todos pueden dar sus aportes para llegar a la solución del problema mencionado.
Decirles también que lo que estoy compartiendo está funcionando con ADO Components, DCOM y MIDAS. La idea es que funcione con DATASNAP.

Explico el ejemplo que estoy compartiendo:

La idea es hacer en DataSnap una forma de hacer actualizaciones de multiples Datasets hacia el servidor con una sola llamada y todo esto sea controlado en una sola transacción. Para evitar que por cada Dataset se haga un APPLYUPDATE. Que esto significa varias llamadas ya sea por Insertar y/o actualizar un registro en el servidor de base datos. Claro también para decirles que la idea es trabajar con atributos de tipo IDENTITY y con FK. Detallo lo que les comparto:

Servidor:
Se ha creado una conexión a una tabla users que también comparto el script. Dentro del servidor se ha creado un método llamado "UpdateDeltas" que este recibe los nombres de los Providers y Deltas a Actualizar. Si revisan el código ahí verán que todo eso se envía en un arreglo de Variants.

Según las pruebas que he realizado con el ejemplo en la parte del servidor no hay problemas. (Eso Creo...)

Cliente:
Para invocar al método definido en el servidor "UpdateDeltas" se pueden hacer de 2 maneras:

1. Usando el componente TSQLServerMethod que configurando la clase del servidor datasnap se tiene el componente listo para invocar al método.

OJO: Yo he configurado el componente pero no me está funcionando ya que si ven el código desde el cliente se envían 2 parámetros OLEVARIANT y el componente al momento de configurar los parámetros del método los pone como VARIANTs y ahí me sale errores que no soporta los parámetros. SI ALGUIEN CAPAZ PUEDE VER QUE PUEDE ESTAR PASANDO... no creo que los de embarcadero no hayan considerado los parámetros OLEVARIANTS para los métodos en DATASNAP.

2. Obteniendo las Clases al Servidor (El algunos foros lo conocen como las clases Proxis) y en este caso si me está permitiendo invocar el método definido en el servidor y poder ejecutarlo con los parámetros OLEVARIANTS. Cosa rara porque se supone que es lo mismo. Ahí espero sus comentarios.

Continuando después de definido la llamado al método del servidor se ha definido un procedimiento llamado: ApplyUpdates que ahí lo que está haciendo es preparar la lista de Deltas y Providers los cuales van a ser enviados al Servidor por el método definido en los puntos 1 ó 2.

Espero haya podido ser un poco claro en la explicación, por ello mejor les he enviado el ejemplo para que se les haga más fácil entenderme.

El ejemplo que les comparto todo esta funcionando con la excepción de un procedimiento ReconcileDeltas que lo que hace es de todos los Deltas actualizados en el servidor actualizarlos en los ClientDataSets correspondientes. Aquí me está saliendo el problema y me muestra un error: “Mismatch in datapacket."

Amigos espero no haberlos confundido, sigo investigando cual puede ser la causa de los 2 problemas que se me han presentado tanto en usar el componente TSQLServerMethod con parámetros OLEVARIANTS y al momento de hacer RECONCILE de los Deltas actualizados en la Base Datos.
Capaz ya a alguien les haya pasado algo similar y de verdad resolver lo que he publicado nos va ayudar bastante a minimizar las llamadas al servidor.

Muchas gracias y Saludos.

Maniche ^\||/

Maniches
06-11-2012, 00:18:43
Amigos me confirman si han podido ver el Archivo Adjunto, Lo hice al momento de responder pero no lo veo. Ojala Uds. Si.

Si no pudieron ver. Como puedo subirlo al foro? al tema?

Gracias.

Casimiro Notevi
06-11-2012, 00:24:15
Amigos me confirman si han podido ver el Archivo Adjunto

Hola, ¿a qué archvo adjunto te refieres?, ¿de qué tipo es?, ¿cuánto ocupa?

Maniches
06-11-2012, 00:30:23
Lo que pasa casimiro que al momento de responder al Tema he creado un ejemplo y lo he adjuntado a la respuesta. Me refería a ese archivo si Uds. del foro los pueden ver.

La verdad que no domino mucho las opciones de este foro por ello que opte por preguntar.

Espero me comprendas ahora.

Casimiro Notevi
06-11-2012, 00:42:41
Cuando estás creando un mensaje cualquiera, por ejemplo si contestas este mismo, verás un poco más abajo la opción de "adjuntar archivo", puede ser un zip, rar, etc. aunque el tamaño es pequeño.
Por eso te pregunto ¿qué tipo de archivo es y qué tamaño tiene?
Además que me parece recordar que si tienes menos de 10 mensajes todavía no podrás adjuntar nada.
En todo caso puedes enviarlo a, por ejemplo, mi correo y ya lo enlazo yo mismo.
Mi correo... pinchando en el nombre de mi nick, 'Casimiro Notevi', se abrirá un menú y ahí tienes la opción.

Maniches
06-11-2012, 00:56:40
El Archivo es pequeño y capaz es por lo que mencionas que no tengo los mensajes suficientes para poder adjuntar archivos.

Lo he publicado en : https://rapidshare.com/files/3523146110/DSXE3.zip;

Espero todos puedan tener acceso a poder bajarlo.

Me confirman para ver otra forma de hacerlo.

Muchas Gracias por su apoyo.

Casimiro Notevi
06-11-2012, 01:28:46
Sí, se puede descargar, por cierto, ¿qué es?, ¿algún ejemplo?


pd: en teoría tampoco puedes poner enlaces si tienes menos de 10 mensajes :confused:

Maniches
06-11-2012, 16:34:18
Cierto amigo Casimiro es un ejemplo de lo que estaba explicando anteriormente en mis respuestas.

Que bueno que puedan ver el ejemplo anterior así va a permitir que todos aportemos a la solución y nos ayude.

De seguro nos va ayudar a todos. :)

Saludos.

Maniches
08-11-2012, 07:15:27
Cierto amigo Casimiro es un ejemplo de lo que estaba explicando anteriormente en mis respuestas.

Que bueno que puedan ver el ejemplo anterior así va a permitir que todos aportemos a la solución y nos ayude.

De seguro nos va ayudar a todos. :)

Saludos.
Amigos he creado el sgte tema para que todos pongas sus aportes.

Link (http://www.clubdelphi.com/foros/showthread.php?t=81366)

Saludos