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.

19 respostas
  1. Frainer
    Frainer says:

    @ 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

    Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      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 🙂

      Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      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

      Responder
  2. Rodrigo
    Rodrigo says:

    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…

    Responder
  3. Rodrigo Rodrigues
    Rodrigo Rodrigues says:

    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.

    Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      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

      Responder
  4. Luis Rodolfo
    Luis Rodolfo says:

    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…

    Responder
  5. Eric
    Eric says:

    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;

    Responder
  6. Rafael
    Rafael says:

    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.

    Responder
  7. Elvis Hotz
    Elvis Hotz says:

    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!

    Responder
  8. Sinvaldo Francisco
    Sinvaldo Francisco says:

    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

    Responder
  9. Arlei Ferreira Farnetani Junior
    Arlei Ferreira Farnetani Junior says:

    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…

    Responder
  10. Laerdes
    Laerdes says:

    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

    Responder

Deixe uma resposta

Want to join the discussion?
Feel free to contribute!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *


Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.