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;
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”
@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
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?
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
Tudo bem Andreano! Já entendi o esquema…
Mas, fazendo uma pergunta extra: Tentei criar um TSQLConnection em runtime para realizar a conexão DataSnap com a aplicação servidor mas, sempre dá o erro:
TDBXError with message ‘Unknown driver: DataSnap’
Existe alguma dica pra funcionar?
Este erro pode estar relacionado com várias coisas, comece declarando a unit DbxDatasnap no seu código
Em cheio! Resolvido. =))
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!
Existe uma tecnica parecida para publicar os serverClass num DataSnap Rest Application?
Rafael é só usar o mesmo código
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!
Davi,
No create to simpleserverclass tem um parâmetro chamado ExposeProvider, este tem que estar True para que o cliente veja os providers
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?
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