sábado, 21 de abril de 2012

Copiar registros de um ClientDataSet filtrado sem precisar varrê-lo

Introdução

Olá amigos, o blog está de volta e venho com uma dica que já precisei utilizar algumas vezes e que pode ser útil.

Muitas vezes precisamos copiar os dados filtrados de um ClientDataSet(CDS) para outro, mas vemos que a propriedade DATA do CDS guarda TODOS os dados do CDS desconsiderando o filtro, assim como a propriedade XMLDATA, já o CLONECURSOR não cria uma cópia do DATA, ele apenas aponta para o mesmo DATA do CDS filtrado.
Como tempo é dinheiro, acabamos varrendo o CDS e copiando os dados filtrados para outro CDS. Nada elegante, mas funciona.

Uma solução para este problema é utilizar o método SETPROVIDER do CDS para 'copiar' apenas o DATA filtrado de um CDS para outro, veremos abaixo um exemplo prático.

Exemplo prático

Nosso banco para esse teste será o EMPLOYEE do Firebird.

1. Crie uma aplicação nova: File > New > VCL Form Application
2. Adicione os seguintes componentes para exibirmos os clientes em um DBGrid:

TSQLConnection
Name
sqcEmployee

TSQLDataSet
Name CommandTetxt SqlConnection
sqlCliente select * from CUSTOMER sqcEmployee

TDataSetProvider
Name DataSet
dspCliente sqlCliente

TClientDataset
Name ProviderName
cdsCliente dspCliente

TDataSource
Name DataSet
dtsCliente cdsCliente

TDBGrid
Name DataSource
dbgCliente dtsCliente

Neste momento já possuímos os componentes necessários para exibir os dados da tabela CUSTOMER em um DBGrid.

3. Após verificar que os dados estão sendo exibidos corretamente, acrescente um CDS, um Datasource e um DBGrid.
Associe-os conforme quadros abaixo:

TClientDataset
Name
cdsFiltro

TDataSource
Name DataSet
dtsFiltro cdsFiltro

TDBGrid
Name DataSource
dbgFiltro dtsFiltro

4. Agora iremos carregar o cdsFiltro com todos os CUST_NO maior que 1000 que o cdsCliente possuir.
Pra isso basta incluirmos um botão e o seguinte código em seu evento OnClick:
 

  cdsCliente.Filtered := False;
  cdsCliente.Filter := 'CUST_NO > 1010';
  cdsCliente.Filtered := True;

  cdsfiltro.SetProvider(cdsCliente);
  cdsfiltro.Open;


5. Execute a aplicação e confira os dados filtrados do cdsCliente serem exibidos no dbgCliente corretamente.

Conclusão

É isso aí pessoal, um exemplo simples de como evitar varrer um CDS para capturar os registros filtrados.

Qualquer dúvida, crítica ou sugestão deixe seu comentário.
Obrigado e até a próxima!

8 comentários :

  1. Ola Rafa , tudo bem ? Então , apesar de minha humilde opinição o SetProvider não ser para isto , o Delphi tinha que gerar um Raise , mas indepedente disso , como Aplicar (ApplayUpdates) alguma alteração no cdsFiltro a sua base de dados ????

    ResponderExcluir
  2. Olá Marco,
    Como nosso colega Vitor Rubio comentou na lista NDDV esse parece ser um dos "Segredos Ocultos" do Delphi.

    No artigo utilizo um exemplo para mostrar os dados filtrados em um segundo GRID, ou seja, meramente ilustrativo, porém, esse recurso pode ser utilizado em várias outras situações, por exemplo:
    Utilizando ntier, muitas vezes precisei trafegar dados entre a aplicação servidora e a cliente. Dependendo da sua arquitetura e versão do delphi a aplicação cliente muito provavelmente só pode receber dados do tipo oleVarient ou tipos primitivos (A partir da versão 2010 já é possível trafegar objetos).
    Então para passar ou receber dados filtrados de um CDS entre aplicação cliente e servidora, este recurso pode ser utilizado passando como parâmetro o cdsFiltro.Data(OleVariant) ou o cdsFiltro.XMLData(string).

    O que eu quero dizer é que sempre utilizo este recurso para evitar varrer um CDS para copiar seus dados filtrados.

    Confesso que não utilizei este recurso ainda com a finalidade de alterar os dados filtrados via ApllyUpdates, contudo, acredito que a atualização dos dados do exemplo apresentado possam ser feitas através do seguinte código:

    cdsFiltro.SetProvider(dtsCliente);
    cdsFiltro.ApplyUpdates(0);

    Espero que tenha sido claro.

    Gostaria de agradecer pelo seu comentário Masco, pessoas do seu nível de conhecimento só tem a agregar, espero que volte mais vezes.

    Obrigado.

    ResponderExcluir
  3. Entendo a sua intenção . Mas veja o que o Helphi do Delphi acha disso

    "O Provider parâmetro deve ser um descendente de TCustomProvider . Caso contrário, SetProvider gera uma exceção."

    E veja a implementação

    procedure TCustomClientDataSet.SetProvider(Provider: TComponent);
    begin
    if Provider is TCustomProvider then
    AppServer := TLocalAppServer.Create(TCustomProvider(Provider)) else
    if Provider is TDataset then
    AppServer := TLocalAppServer.Create(TDataset(Provider)) else
    AppServer := nil;
    end;

    Na minha opinião deveria gerar mas não vejo isto sendo feito na implementação do Método

    ResponderExcluir
  4. Então Rafael Pimenta , desculpe a insistência . Conheço o Vitor^ de longa data e sei da sua capacidade . Sei ainda que ele sempre preteriu códigos limpos e orientados e procurando o porquê das "coisas" ele dificilmente acomoda e é insistente . Apesar de voce não ter reportado meu último feedback , parece que estou num monologo e invadindo o seu espaço (tentei o seu email e não consegui.) Mas tive pensando nesta dica (apesar que ela cumpre o seu papel , deveria não funcionar de acordo com o help do Delphi) , acho que a melhor forma de fazer isto é atráves mesmo da property DataRequest do clientdadset

    ResponderExcluir
  5. Boa tarde Marco, desculpe não ter respondido ainda seus comentários, essa semana está sendo corrida por estar mudando de emprego.

    Pois bem, sobre seu segundo comentário, vejo que há uma disparidade entre o que se encontra no help e a implementação do setProvider, pois, apesar do help dizer que o Provider parâmetro deve ser descendente de TCustomProvider a implementação mostra que o mesmo também pode ser descendente de TDataSet:

    if Provider is TDataset then
    AppServer := TLocalAppServer.Create( TDataset(Provider))

    Penso que este é um 'recurso' q pode ser usado para esta finalidade, mas com as devidas resalvas.

    Sobre o seu último comentário, como você sugere que o dataRequest seja utilizado para está finalidade?

    Volto a agradecer seus comentários Marco, e fique a vontade para comentar, será sempre um prazer contar com sua opnião.

    Notas:
    .Depois irei atualizar o artigo com as devidas observações apresentadas por você.
    .Meu e-mail é rafa.spimenta@gmail.com
    .Vou atualizar o blog para que o email esteja disponível ao público.
    .Procurei seu e-mail mas não o encontrei em seu blog.

    Forte abraço.

    ResponderExcluir
  6. Rapaz, nao localizei o codigo do onClick (passo 4)

    poderia me informar? Obrigado

    tfiliano@gmail.com

    ResponderExcluir
  7. Olá thiago, acredito ser algum problema no blog e o plugin de código.
    Estarei acertando.
    Obrigado!

    ResponderExcluir