Delphi

Publicando providers durante o registro dinâmico de DataSnap Server Class

Já faz algum tempo que publiquei aqui no blog sobre registro de DataSnap Server Class em tempo de execução, neste mesmo post surgiu vários comentários relacionados a não publicação dos Provider parte do ServerModule, o código original ao registrar o DSServerClass publicava apenas métodos (Server methods), mas não os Providers.

A solução para este problema é bem simples, basta registrar a classe TDSProviderDataModuleAdapter, a qual é usada internamente para permitir que o Provider esteja visível para os clientes DataSnap. TDSProviderDataModuleAdapter atua como um proxy entre TProviderDataModule no lado servidor e o TDSProviderConnection no lado cliente.

Poderíamos apenas mudar a implementação do método GetDSClass conforme abaixo, isso funcionaria, mas não seria a melhor implementação visto que estariamos sempre publicando os métodos da interface IAppServer utilizados pelo Provider e ClientDataSet, não faz sentido publicar esta interface se não há provider no ServerModule.

Tornar a publicação parametrizável traz maior flexbilidade e controle para a aplicação e para o desenvoldedor, fica a seu critério quando publicar ou não os providers, assim sendo precisamos apenas de um novo parâmetro na classe TSimpleServerClass.

function TSimpleServerClass.GetDSClass: TDSClass;
begin
  Result := TDSClass.Create(FPersistentClass, False);
  Result := TDSClass.Create(TDSProviderDataModuleAdapter, Result)
end;

Abaixo temos o código atualizado, a única alteração foi no constructor que recebeu o novo parâmetro ExposeProvider, desta forma você decide se quer ou não publicar os providers.

type

unit SimpleServerClass;

interface

uses DSServer, Classes, DSCommonServer, DSReflect;

type

  TSimpleServerClass = class(TDSServerClass)
  private
    FPersistentClass: TPersistentClass;
    FExposeProvider : Boolean;
  protected
    function GetDSClass: TDSClass; override;
  public
    constructor Create(AOwner: TComponent; AServer: TDSCustomServer;
      AClass: TPersistentClass; ExposeProvider: Boolean; ALifeCycle: String);
      reintroduce; overload;
  end;

implementation

constructor TSimpleServerClass.Create(AOwner: TComponent;
  AServer: TDSCustomServer; AClass: TPersistentClass; ExposeProvider: Boolean;
  ALifeCycle: String);
begin
  inherited Create(AOwner);
  FPersistentClass := AClass;
  FExposeProvider  := ExposeProvider;
  Self.Server := AServer;
  Self.LifeCycle := ALifeCycle;

end;

function TSimpleServerClass.GetDSClass: TDSClass;
var
  isAdapted : Boolean;
begin
  isAdapted := FPersistentClass.InheritsFrom(TProviderDataModule);
  Result := TDSClass.Create(FPersistentClass, isAdapted);
  if FExposeProvider and isAdapted then
     Result := TDSClass.Create(TDSProviderDataModuleAdapter, Result)
end;

end.

Abaixo um exemplo onde são registradas 3 classes e somente uma delas expõe os Providers:

procedure RegisterServerClasses(AOwner: TComponent; AServer: TDSServer);
begin
  Assert(AServer.Started = false, 'Não é possível adicionar classes com o servidor ativo');

  TSimpleServerClass.Create(AOwner, AServer, TGlobal, False, TDSLifeCycle.Server);
  TSimpleServerClass.Create(AOwner, AServer, TCustomer, True, TDSLifeCycle.Session);
  TSimpleServerClass.Create(AOwner, AServer, TObjectPool, False, TDSLifeCycle.Invocation);
end;
15 respostas
  1. Júlio César Ferreira (@jcmferreira)
    Júlio César Ferreira (@jcmferreira) says:

    Bom código mas, a implementação não funciona!

    Uma cópia exata do código descrito acima para tentar publicar uma classe TUsuario que possui um objeto TDataSetProvider e que precisa ser exposto.

    Na aplicação cliente, quando é tentado referenciar o ProviderName do componente TClientDataSet, dá erro de “Invalid class typecast”

    Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      @Júlio,

      O código está correto e funciona perfeitamente quando o DataModule tem providers.

      No seu caso você está tentando passar um provider em uma classe, TDataSetProvider não é suportado para passar. Você neste caso tem que usar o TDataSetProvider no datamodule.

      Aproveitando atualizei a implementação do exemplo neste post

      Responder
  2. Júlio César Ferreira (@jcmferreira)
    Júlio César Ferreira (@jcmferreira) says:

    Andreano,

    Pelo que entendi então, somente poderei disponibilizar um TDataSetProvider se ele estiver dentro de um TDataModule?
    Neste caso, sou obrigado a herdar sempre de um TDataModule ou dos seus descendentes?
    Existe algum meio para fazer isso de uma classe normal, herdando de TPersistent por exemplo?

    Responder
  3. Andreano Lanusse
    Andreano Lanusse says:

    Julio,

    É só mudar o código e não chegar a herança, o código está assim porque esta é a forma que o DataSnap recomenda e caso você queira expor provider a classe checa se realmente existe um Datamodule associado.

    Eu não tenho detalhes do que você está desenvolvendo, mas DataModule para situações de provider é o melhor caminho

    Responder
  4. Tiago
    Tiago says:

    Fazendo uns testes, onde queria expor uma classe herdade de TProviderDataModule. Porém, não executa o método Constructor Create, onde faço a implementação da criação de meu TDataSetProvider e do TSQLDataSet.


    {$METHODINFO ON}
    TCustomServerClass = Class(TProviderDataModule)
    Published
    DSProvider:TDataSetProvider;
    SQLDataSet:TSQLDataSet;
    Public
    Constructor Create(AOwner:TComponent); Override;
    Destructor Destroy(); Override;
    End;
    {$METHODINFO OFF}

    Constructor TCustomServerClass.Create(AOwner:TComponent);
    Begin
    Inherited;
    DSProvider := TDataSetProvider.Create(Self);
    SQLDataSet := TSQLDataSet.Create(Self);
    DSProvider.DataSet := SQLDataSet;
    SQLDataSet.SQLConnection := UntDMServer.DMServer.SQLConnectServer;
    SQLDataSet.CommandType := ctQuery;
    End;

    Tem algo que eu possa implementar para executar o método Constructor Create ?
    Grato!

    Responder
  5. davi
    davi says:

    Andreano, desculpe minha ignorância mas apesar de usar o delphi desde a versão 4, só estou entrando para o mundo do datasnap agora, até então usava servidores baseados na tecnologia com com o remotedatamodule.

    To com uma aplicação onde o server tem uma classe X que é de um tdatamodule, na unit uServerContainer tenho um DSServerClass que retorna a classe X, no lado client o dbconnection conecta normalmente ao servidor quando o mesmo está rodando, contudo mesmo configurando o dsproviderconnection com a classe X não consigo visualizar o provider do lado servidor. O seu artigo parece tratar sobre isso, mas não entendi onde exatamente faço a alteração do código? Esta classe TSimpleServerClass seria a minha classe X??? algum fonte exemplo simples pra me indicar? grato!

    Responder
  6. Denis
    Denis says:

    Andreano, gostaria de saber se tem como fazer algo semelhante ao que foi feito aqui, porém na parte cliente. Porque chamar aquela opção do SQLConnection de “generate datasnap client classes” e ele gerar um única unit enorme com tudo publicado é dureza para o trabalho em equipe. Seria ideal ter units menores correspondentes a cada TServerModule. O que você acha?

    Responder
  7. Rodrigo cesr
    Rodrigo cesr says:

    Olá, não entendi onde devo chamar a procedure RegisterServerClasses(AOwner: TComponent; AServer: TDSServer);, estou utilizando Datasnap Rest, e queria disponibilizar os providers contidos em um TDSServerModule na minha aplicação. obrigado

    Responder

Trackbacks & Pingbacks

Deixe uma resposta

Quer participar da discussão?
Fique a vontade para contribuir!

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>