PDA

Ver la Versión Completa : DataSnap varios clientes conectados a distintas db por un mismo servicio


Soa Pelaez
04-05-2018, 22:47:36
Buen día.

Estoy manejando una aplicación cliente/servidor con la tecnología DataSnap, requiero que cuando se conecte el cliente el ID de la session se mantenga hasta que cierra completamente la aplicación, leyendo los ciclos de vida del server http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Server_Class_LifeCycle establecí mi server en session y en cada invocación se me modifica el id de la session del cliente, y requiero que no se modifique el id de la session hasta que el cliente se salga.

Gracias.

Soa Pelaez
07-05-2018, 14:48:28
Buen dia



Tengo una duda, es posible conectar de forma dinamica varios clientes que acceden a distintas bases de datos por un mismo servicio en datasnap.


De antemano muchas gracias.


https://www.clubdelphi.com/foros/showthread.php?t=93037

gatosoft
09-05-2018, 05:41:29
Buen día Soa,

Bien ha hecho Casimiro en hacerte seguimiento con los hilos que has creado respecto al mismo tema, ya en uno de esos hilos te habia mostrado un enlace muy bueno que hablaba de los LifeCycles de DataSnap (https://community.embarcadero.com/article/technical-articles/162-programming/3006-delphi-labs-datasnap-xe--server-methods-lifecycle) y creo que allí está muy claro lo que necesitas. Pero como no hemos visto código tuyo o algo que nos ayude a indicar donde está el error pues nos toca a nosotros suponer y hacer la tarea... (Lo digo de la forma mas amistosa)

Creo que tu problema es el lugar donde este ubicando tus componentes de base de datos y la clave está en el componente TServerMethods, asi:

Del lado del server:
===============
*) Create tu servidor normal.

*) Tienes un ServerMethodsUnit y Un ServerContainerUnit, el primero tiene la clase y la funcionalidad que vas a exportar y el segundo tiene los componentes de conexión y transporte (normal)

*) Verifica que en el SeverContainer tu componente DSServerClass1 tenga la propiedad LifeCycle = 'Session' (ese es el valor por defecto).

*) En algún lugar de tu programa debes tener tus componentes de BD. Para este caso, yo creo un TDataModule, y ahi ubico mi componente Connection y mi componente Query. Configuras y pruebas la conexión (Normal)

*) Volvemos al serverMehds y allí creamos una variable de tipo TDatamule que acabas de crear.

*) Defines el constructor y el destructor heredados del TServerMethods y creas dos funciones, una es para Conectar a la BD, y le vas a pasar como parámetro el ID de la BD que quieras.. tu defines como identifias tus BDs, la otra función es para probar la conexión trayendo una consulta: (Y utilizo los componentes UniDac)

unit ServerMethodsUnit1;

interface

uses System.SysUtils, System.Classes, Datasnap.DSServer, Datasnap.DSAuth, uDTM;

type
{$METHODINFO ON}
TServerMethods1 = class(TComponent)
private
{ Private declarations }
FSoyUnID: String;
FDtm: TDataModule1;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
Destructor Destroy; Override;
function getID: String;
Function ConectarBD(pBaseID: String): String;
Function Consultar: String;
end;
{$METHODINFO OFF}

implementation


uses System.StrUtils;

function TServerMethods1.ConectarBD(pBaseID: String): String;
begin
Result:= 'Conectado';
Try
FDtm.UniConnection1.Connected:= False;
if pBaseID.ToUpper = 'BASE1' then //Si el parametro es BASE1 conecta a TEST, o sino a QA
FDtm.UniConnection1.Database:= 'BaseTEST' //Aqui configuras lo que tengas que configurar para conectarte (usuario, pwd, puerto, base)
else
FDtm.UniConnection1.Database:= 'BaseQA';
FDtm.UniConnection1.Connected:= True;
Except
On E: Exception do
Result:= E.Message;
End;
end;

function TServerMethods1.Consultar: String;
begin
//pues eso.... consulta un query... las dos BDs tiene la misma estructura de tablas pero diferente data
Try
Fdtm.UniQuery1.Close;
Fdtm.UniQuery1.SQL.Text:= 'Select * from prueba1';
FDtm.Clientdataset1.Close;
FDtm.Clientdataset1.Open;
Result:= FDtm.ClientDataSet1.XMLData;
Except
On E:Exception do
Result:= 'ERROR '+e.Message;
End;

end;

constructor TServerMethods1.Create(AOwner: TComponent);
begin
inherited;
Randomize;
FSoyUnId:= Random(10000).ToString;//esto es emulando lo del ejemplo del link que te di...
FDtm:= TDataModule1.Create(Self); //Aqui creas tu DTM
end;

destructor TServerMethods1.Destroy;
begin
FDtm.UniConnection1.Connected:= False;
FDtm.Free; //Aqui destruyes el dtm
inherited;
end;

function TServerMethods1.getID: String;
begin
Result:= FSoyUnId; //basura...
end;

end.


En el cliente:
==========

*) Tengo un memo para escribir mensajes
*) un grid, un clientdataset (que recibe la consulta del server enviada como XML), el datasource por supuesto
*) tres botones: "getId", para traer el Id de la sesion (no es relevante), "Conectar", en donde enviamos el parametro para configurar y conectar la DB y "consultar" para traer la consulta del server (No Utilizo el DSProviderConnection paar simplificar el tema)

unit frmPrincipalCLI;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Data.DBXDataSnap,
Data.DBXCommon, IPPeerClient, Data.DB, Data.SqlExpr, ProxyDS,
Datasnap.DBClient, Vcl.Grids, Vcl.DBGrids;

type
TForm2 = class(TForm)
Button1: TButton;
Memo1: TMemo;
SQLConnection1: TSQLConnection;
Button2: TButton;
Edit1: TEdit;
Button3: TButton;
DBGrid1: TDBGrid;
ClientDataSet1: TClientDataSet;
DataSource1: TDataSource;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
aClient: TServerMethods1Client;
end;

var
Form2: TForm2;

implementation

{$R *.dfm}


procedure TForm2.Button1Click(Sender: TObject);
begin
Memo1.Lines.Add(aClient.GetID); //obtiene el Id de la session
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
Memo1.Lines.Add( aClient.ConectarBD(Edit1.Text)); //conecta a la BD y muestra el mensaje "conectado" o un error si lo hubo
end;

procedure TForm2.Button3Click(Sender: TObject);
Var vXML: String;
begin
vXML:= aClient.Consultar; //Trae el XML desde el server y lo asigna al CDS
Try
ClientDataSet1.Close;
ClientDataSet1.XMLData:= vXML;
ClientDataSet1.Open;
Except
Memo1.Lines.Add(vXML); //si hubo error muestra el xml enviado por el server
End;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
aClient.Free; //libera el proxyDS
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
if not SQLConnection1.Connected then
SQLConnection1.Connected:= True; //conecta el server datasap

aClient := TServerMethods1Client.Create(SQLConnection1.DBXConnection); crea el proxyDS
end;

end.



y ya.. despues de eso obtines lo que quieres...

el punto es que el ServerMethods es el componente que se aisla y todo loq ue se defina ahi se va a comportar dependiendo de LifeCycle que definas (Servr, Session o invocación)

y cuando recuerde como subir una imagen al foro, te adjunto el resultado de mi prueba...

editado: aqui la imagen (https://ibb.co/hppidn)

Saludo,

gatosoft
09-05-2018, 14:27:54
Como te habìa comentado en otro hilo (https://www.clubdelphi.com/foros/showthread.php?t=93063), al no tener parte de tu codigo no podemos mas que suponer...

En el hilo que te comento, te presenté un ejemplo que hice para el problema que tenias con la conexión a BD, que es exactamente el mismo problema que debes estar teniendo con tu ID de session. No veo como te puede fallar si haces lo mismo que se hace en el artítulo de embarcadero que mencionas....

El quid, está dejar las variables en el TServerMethods... y configurar el DSServerClass como indicas LifeCycle = Session... si no te funciona, déjanos ver tu código para poderte ayudar mejor

saludo,

Soa Pelaez
09-05-2018, 14:51:31
Gracias [gatosoft] si con el ejemplo que me habías compartido https://community.embarcadero.com/article/technical-articles/162-programming/3006-delphi-labs-datasnap-xe--server-methods-lifecycle logre realizar lo que necesitaba, solo quería saber si lo que estaba intentando hacer era lo viable o había otra manera, igual ese ejemplo que colocas esta bueno para alguien mas que lo necesite.

Muchas gracias.

Soa Pelaez
11-05-2018, 23:25:50
Como te habìa comentado en otro hilo (https://www.clubdelphi.com/foros/showthread.php?t=93063), al no tener parte de tu codigo no podemos mas que suponer...

En el hilo que te comento, te presenté un ejemplo que hice para el problema que tenias con la conexión a BD, que es exactamente el mismo problema que debes estar teniendo con tu ID de session. No veo como te puede fallar si haces lo mismo que se hace en el artítulo de embarcadero que mencionas....

El quid, está dejar las variables en el TServerMethods... y configurar el DSServerClass como indicas LifeCycle = Session... si no te funciona, déjanos ver tu código para poderte ayudar mejor

saludo,

Con lo realizado si se me conecta a las distintas db pero al momento de que cada cliente realiza una transacción y me debe retornar un numero de transacción solo retorna la del ultimo que realiza la transacción no me respeta la sessión de cada uno.

gatosoft
11-05-2018, 23:35:33
Con lo realizado si se me conecta a las distintas db pero al momento de que cada cliente realiza una transacción y me debe retornar un numero de transacción solo retorna la del ultimo que realiza la transacción no me respeta la sessión de cada uno.


Habría que ver un ejemplo de lo que hablas... ¿dices que ingresas a dos bases distintas, pero al hacer una transacción ambos programas retornan la misma información? (¿te refieres a ejecutar un ExceSQL, o a un startTransacction?, o a qué?).


Necesitamos código ejemplo

Soa Pelaez
15-05-2018, 14:46:22
Habría que ver un ejemplo de lo que hablas... ¿dices que ingresas a dos bases distintas, pero al hacer una transacción ambos programas retornan la misma información? (¿te refieres a ejecutar un ExceSQL, o a un startTransacction?, o a qué?).


Necesitamos código ejemplo

Realizó lo siguiente, luego de guardar retorno una variable con un número del registro que guardo y eso lo devuelvo al cliente, pero cuando el cliente A y el cliente B le dan guardar al mismo tiempo, uno de los
dos pierde el registro e igual no retorna el número.


Conexion.StartTransaction;
try
iErrors := Squema.ApplyUpdates(0);
if iErrors = 0 then
begin
for x := 0 to Squema.Count - 1 do
if Squema.DataSets[x].UpdatesPending then
begin
Squema.DataSets[x].CommitUpdates;
Conexion.Commit; //09/12/2015
_ResultadoMD := 1;
end;
end
else
begin
_ResultadoMD := 0;
Conexion.Rollback;
end;
except
on e: Exception do
begin
Conexion.Rollback;
//Aqui si el número existe lo aumenta e inicia nuevamente una transacción
end;
end;
//Aqui retorno el número guardado para enviarselo al cliente por medio de una función que retorna un entero.
retNumeroDoc()

Soa Pelaez
15-05-2018, 19:00:21
Realizó lo siguiente, luego de guardar retorno una variable con un número del registro que guardo y eso lo devuelvo al cliente, pero cuando el cliente A y el cliente B le dan guardar al mismo tiempo, uno de los
dos pierde el registro e igual no retorna el número.


Conexion.StartTransaction;
try
iErrors := Squema.ApplyUpdates(0);
if iErrors = 0 then
begin
for x := 0 to Squema.Count - 1 do
if Squema.DataSets[x].UpdatesPending then
begin
Squema.DataSets[x].CommitUpdates;
Conexion.Commit; //09/12/2015
_ResultadoMD := 1;
end;
end
else
begin
_ResultadoMD := 0;
Conexion.Rollback;
end;
except
on e: Exception do
begin
Conexion.Rollback;
//Aqui si el número existe lo aumenta e inicia nuevamente una transacción
end;
end;
//Aqui retorno el número guardado para enviarselo al cliente por medio de una función que retorna un entero.
retNumeroDoc()


Y algo peor, me mezcla los registros que vienen del Cliente A con los del cliente B.

gatosoft
16-05-2018, 18:10:53
Pareciera que no has solucionado tu error original. Me queda una duda:

1) A y B apuntan a bases de datos separadas (de acuerdo a un post anterior)
2) Cuando guardas A y B al mismo tiempo se pierde uno de los registros, y se mezclan los datos.

¿cuando ejecutas A y B por separado puedes verificar que los datos se guarden en BDs diferentes?

Sigo creyendo que el problema está en el lugar donde defines tus variables (Fuera del ClassMethods tal vez) por lo que puedes estar compartiendo recursos entre servidores.

¿donde defines la conexión?, en un dtm externo... está bien, pero recuerda que tu classMethods debe trabajar su propia instancia....