DataSnap – Compartilhando conexão com o banco entre diferentes Server Classes a partir do mesmo cliente (Session)
Aqueles que começam a desenvolver aplicações utilizando DataSnap tem como hábito definir conexões ao banco de dados por Data Module, isso gera um volume muito grande de conexões ao banco de dados, dependendo da situação isso pode se tornar um problema.
No Delphi XE o DataSnap traz o gerenciamento de sessão (Session Management), este gerenciamento facilita a implementação do servidor em diversos aspectos, especificamente tratando do tema de como definir uma única conexão ao banco de dados para cada cliente onde cada Server Methods receba esta conexão, o gerenciamento de sessão é o ponto chave pra isso.
A definição deste controle irá ocorrer do lado servidor, a aplicação cliente não precisa e não tem que saber como ocorre a conexão ao banco de dados.
Ao criar um servidor DataSnap como boa prática definimos um Server Container (Data Module) que basicamente é o seu servidor DataSnap, é neste container que será definido o método responsável por passar a cada Server Class a conexão com o banco, assim sendo, a implementação seria.
Primeiro, o método GetConnection retornará a conexão ao banco, neste método teremos a lógica responsável por saber se a conexão existe ou não para aquele cliente conectado, cada cliente ao conectar so servidor DataSnap terá um ID de sessão associado, assim declaramos o método e a variável ListofConnection como parte do nosso Server Container.
private { Private declarations } ListofConnection : TDictionary; public function GetConnection : TSQLConnection;
O método GetConnection irá consultar o pool de conexões (variável ListOfConnection) para saber se este cliente já tem uma conexão no pool ou não, através do TDSSessionManager.GetThreadSession.Id obtemos o Id da sessão para este cliente, este Id é único e somente será renovado se o cliente desconectar e voltar a conectar novamente ao servidor.
Não existindo a conexão para o cliente naquele sessão, será criada dinamicamente uma conexão e esta será retornada.
function TServerContainer1.GetConnection: TSQLConnection; var dbconn : TSQLConnection; begin if ListofConnection.ContainsKey(TDSSessionManager.GetThreadSession.Id) then Result := ListofConnection[TDSSessionManager.GetThreadSession.Id] else begin dbconn := TSQLConnection.Create(nil); dbconn.Params.Clear; dbconn.LoadParamsOnConnect := true; dbconn.ConnectionName := 'DS Employee'; ListofConnection.Add(TDSSessionManager.GetThreadSession.Id, dbconn); Result := dbconn; end; end;
A partir de agora precisamos informar a cada Server Class qual conexão utilizar. Se o seu Server Class tem como base um DataModule, basta no onCreate do DataModule informar a conexão que deverá ser utilizada nos objetos TSQLQuery ou outro objeto que conecte ao banco de dados.
Como exemplo, tenho um DataModule como Server Class, neste existem 2 SQLQuery, assim sendo passo a conexão para cada uma deles:
COUNTRYQuery.SQLConnection := ServerContainer1.GetConnection; EMPLOYEE1Query.SQLConnection := ServerContainer1.GetConnection;
Para não deixar conexões abertas no servidor assim que um cliente desconectar do servidor DataSnap, implementamos no evento onDisconnect do componente DSServer o seguinte método:
if GetConnection <> nil then GetConnection.Close;
Este método irá fechar a conexão com o banco.
O código fonte completo está disponível no Code Central.
Hahahaha…. implementei esse controle no braço! Valeu Andreano!
Próximo passo, retirar o controle manual e utilizar o nativo…
@ off Topic
Bom dia Andreano!
Desculpa incomodar, mas estou com um problema aqui que não estou encontrando solução, já pesquisei muito e nada.
Tenho uma aplicação que faz uso da zlib, sempre funcionou perfeitamente, mas agora com delphi 2010, não consigo mais fazer a descompactação do arquivo. Creio eu que ter algo haver com o unicode, pois o nome dos arquivos estão vindos com caracteres invalidos.
Voce teria alguma solução para esse problema?
Obrigado,
Frainer
Oi Frainer,
Coloca essa dúvida na lista e também um exemplo do problema, assim vai ser mais fácil te ajudar e antes disso entender o problema 🙂
Andreanno,
Você tem algum exemplo de como implementar isso no Delphi 2010?
Grato
Oi Rodrigo,
Seria quase a mesma coisa, apenas mudando a forma com que se captura o ID da Thread, como no Delphi 2010 não tinha o Session Manager, você pode obter o ID da thread através do método GetCurrentThreadID
Andreano,
Se colocar um SQLConnection em um ServerModule e no ServerClass desse ServerModule definirmos o LifeCycle como server, não teria o mesmo efeito?
todos os outros ServerModule estaria linkado a esse ServerModule que contém o LifeCycle = Server…
Oi Andreano, tive no encontro da embarcadero em Natal, onde através de Marcelo, adquirir uma licença do delphi 2010.
Gostaria de saber como fazer para pegar a quantidade de sessões ativas no servidor, se vc pude me responder eu agradeceria bastante, migrei meus sistemas para datasnap 2010 e a quantidade de informação disponível a respeito é muito pouca, fora os seus, os demais conteúdos são muito básicos.
Rodrigo, tem muita informação no EDN.
Para saber a quantidade de sessões ativas você terá que controlar pelos eventos onConnect/onDisconnect do DataSnap Server
Muito obrigado Andreano! Era o que eu precisava…
e outra coisa, fiz uma alteração no evento onDisconnect .
if GetConnection nil then
begin
GetConnection.Close;
GetConnection.Free;
ListofConnection.Remove(GetCurrentThreadID);
end;
Tem algum problema? Utilizo Delphi 2010…
Se você colocou no onDisconnect do server não tem problema.
Bom dia Andreano,
Não consigo utilizar o TDictionary no delphi 2010. Tem alguma diferença na declaração:
Obrigado.
Caro Andreano,
Estou recebendo o mesmo ID para todas as sessões, mesmo com o TDSServerClass.LifeCycle = Session.
Fiz uma aplicação cliente win32 e rodando ela em máquinas diferentes simultaneamente recebo o mesmo SessionID, tens ideia do que possa ser?
Segue fonte da minha classe que devolve o session ID.
unit uServer;
interface
Uses Classes, DsService;
type
{$MethodInfo ON}
TServer = class(TPersistent)
public
function GetSessionID: Integer;
end;
{$MethodInfo OFF}
implementation
{ TServer }
function TServer.GetSessionID: Integer;
begin
Result := TDSSessionManager.GetThreadSession.Id;
end;
end.
Aqui o trecho de código da aplicação cliente Win32, utilzando classe proxy e um objeto SqlConection apontando para o servidor DataSnap.
procedure TFrmPrincipal.Button1Click(Sender: TObject);
var
pServer: TServerClient;
begin
SQLConnection1.Open;
pServer := TServerClient.Create(SQLConnection1.DBXConnection);
Edit1.Text := IntToStr(pServer.GetSessionID);
pServer.Free;
SQLConnection1.Close;
end;
Olá Andreano, estou usando o compartilhamento da conexão pela propriedade ServerConnection do driver Datasnap no Delphi XE…mas ao fazer select com campos blob da erro “Range Check Error” no lado cliente…basta retirá-los e tudo funciona, inclusive já apliquei os hotfixes para o XE…ainda assim o erro existe, queria fazer assim pra não precisar sair mudando os ProviderNames dos meus ClientDatasets…alguma sugestão ? Obrigado.
Boa Tarde
Muito Bom o seu Site, Excelente Conteúdos,
Gostaria de pedir se teria como você fazer um tutorial Ensinando a fazer um servidor DataSnap Com Firedac. Ou se voce sabe onde eu encontro alguma coisa nesse assunto para me ajudar. Desde Já Agradeço!
Thank you. very good article after left Embarcadero. Your articles are very useful. Please, write more about DataSnap.
Tou Construindo um Pequeno Sistema multicamadas mais tou tendo um Erro e não consigo resolver o erro é o Seguinte:
O Banco de dados esta hospedado em um Servidor de um amigo Meu.
* A porta 3050 do Firebird esta liberada.
* Desenvolvi o Servidor ele Rodou Perfeitamente, tanto na maquina local quanto em outra maquina via internet.
* Tou desenvolvendo a parte Cliente só que, quando eu rodo na maquina que estou desenvolvendo o Servidor e a parte cliente funciona, mais quando coloco o servidor para outra maquina, o Servidor Funciona mais o lado cliente não Funciona da Erro de Socket, e também se coloco o Servidor e a parte cliente para outro maquina o Servidor Funciona mais a Parte cliente Não Roda.
Acho que os Erro é só na parte cliente.
Se poder me ajudar fico Grato
Este exemplo também serviria para um Servidor do tipo REST? Tentei jogar o firedac ao invés do sqlconnection, porém quando vai verificar o if ListofConnection.ContainsKey(TDSSessionManager.GetThreadSession.Id) then já me resulta em erro de violação de memória, e quando comento esse if ele me dá um erro no ListofConnection.Add(id, dbconn); O que pode ser? Estou criando normalmente o ListofConnection no oncreate do container. Parece que o erro é causado por alguma coisa nesse TDictionary…
Oi Andreano garimpando na internet em busca de exmemplo de como implementar servidor de aplicação DataSnap encontei esse link eu utilizo o Delphi 2010 ao conpilar esta reclamando “[DCC Fatal Error] DSServerContainer.pas(8): F1026 File not found: ‘DSAuth.dcu'” o que eu poderia fazer para resolver