O foco principal deste artigo é como definir os parâmetros Reset ; KeepSettings do método cloneCursor . O que esses parâmetros trazem de diferente ao se clonar um clientDataSet ? Ou mesmo , o que pode ocasionar uma escolha mau feita ou uma desatenção na hora de definirmos esses parâmetros ? Antes disto vamos a uma breve introdução do método em si
Quando você clona os dados de um ClientDataSet , você cria não apenas um ponteiro adicional para um armazenamento de memória compartilhada, mas também uma visão independente dos dados. Quando falo de ponteiros , me refiro a necessidade de deslocarmos todos os dados sem perdermos a localização do ponteiro inicial ou vice versa .Quando falo de visão independente de dados , falo no sentido de podermos aplicar filtros , índices , ranges nesta nova visão de dados sem afetar a visão dos dados iniciais ou vice-versa . Apesar de termos uma Visão diferente não se pode modificar os dados de um sem afetar o conjunto de dados do Outro , pois ele é compartilhado na memória. Toda alteração , inserção , eliminação em um , automaticamente será refletidas para o outro e vice versa .
Vamos a definição do método
procedure CloneCursor(Source :TCustomClientDataSet; Reset: Boolean; KeepSettings: Boolean = False);
Nos exemplos que se seguem iremos sempre referenciar o Objeto Ativo ClientDataSet o qual iremos clonar de CdsFonteDeDados e o Objeto que receberá essa fonte de dados de CloneCds
Para usa-la fazemos assim:
cloneCds:TClientDataSet;
begin
cloneCds.CloneCursor(CdsFonteDeDados,Reset ??,KeepSettings ???)
end;
O primeiro parâmetro é a fonte dos dados que vc quer clonar ou obter . Este argumento tem que ser um ClientDataSet ativo e que aponte para o armazenamento de memória que vc quer acessar . Muito intuitivo este argumento e dispensa comentários adicionais
O Segundo parâmetro atua juntamente com o terceiro . Porém se pudéssemos aqui utilizar uma analogia com a genética , diríamos que o segundo parâmetro é o gene dominante . de modo que o terceiro parâmetro só vai influenciar se o segundo parâmetro for recessivo . Então se o Reset:=True (Dominante) , indepedentemente do valor que se atribui para KeepSettings (False ou True) , a característica do Clone já esta traçada e não há como fazer mais nada . Então para efeito de estudo passamos a ter um conjunto de Três situações possíveis que devemos analisar e comentar que são :
- Reset:= False ; KeepSettings:=False ****
- Reset:= False ; KeepSettings:=True
- Reset:= true ; KeepSettings:=False ou KeepSettings:=True **** Tanto faz
vamos começar com) Reset igual a true .. KeepSettings (False ou True) tanto faz
O Parâmetro Reset como o próprio nome já diz , vai resetar toda e qualquer propriedade que possa já existir (no sentido de que já foi definida) no Clone . Que propriedades seriam estas ? Por exemplo os valores de IndexName (ou IndexFieldNames), filter, filtered, MasterSource, MasterFields, OnFilterRecord e ProviderName
Leia com muita atenção , pois isto é importante e vou prova-lo . Imagine que vc tenha um clientDataSet com a propriedade ProviderName definida com o nome de algum TDataSetProvider . Imagine ainda que vc definiu no evento BeforeUpdateRecord deste Provider um método inerente a sua necessidade , de modo que quando vc fizer um clientDataSet.ApplyUpdates este evento seja disparado . Ai vc usa um uma fonte de dados e o repassa a este clientDataSet definindo no método o Reset como True . Mas na hora do ClientDataSet.ApplyUpdates , as coisas não ocorrem do jeito que vc pensou e depois de quatro horas (projeto grande cheio de herança..) , vc descobre que o evento BeforeUpdateRecord não esta sendo executado … Mas como ???? A resposta pode agora ser simples , mas nem tanto obvia para quem desconhece o parâmetro Reset . Ao utilizar o Reset = True , as propriedades definidas no clientDataSet serão resetadas , e entre essa propriedades está o ProviderName . Neste caso o ProviderName estará em Branco e vc não conseguira aplicar (comitar) no Banco de Dados (ApplyUpdates)
Segundo caso) Reset:= False ; KeepSettings:=False
O Reset sendo False o método passa a olhar para a definição do terceiro parâmetro para definir o comportamente do clone . Como o terceiro parâmetro nesta situação tb é Falso , o gene dominante será o Reset . O Reset=false implica que as propriedades que o clone usará são as que forem definidas (forem ou estão) pelo clientDataSetFonte . Que propriedades seriam essas ? Mais uma vez , os valores de IndexName (ou IndexFieldNames), filter, filtered, MasterSource, MasterFields, OnFilterRecord e ProviderName .
Agora pense novamente na situação levantada acima , onde vc tem clientDataSet com a propriedade ProviderName definida com o nome de algum TDataSetProvider . Imagine ainda que vc definiu no evento BeforeUpdateRecord deste Provider um metodo inerente a sua necessidade , de modo que quando vc fizer um clientDataSet.ApplyUpdates este evento seja disparado . Irá ocorrer novamente um problema , porque o ProviderName que foi definido originalmente pelo clone será apagado (sobre escrito) pelo ProviderName que foi definido pelo clientDataSetFonte e o tão esperado evento BeforeUpdateRecord não será disparado . Por conta da sobreposição o ProviderName do Clone aponta (na verdade não se trata de um ponteiro mas por hora pensemos assim) para o TDataSetProvider que foi definido no CdsFonteDeDados , e é o evento BeforeUpdateRecord deste Provider que será excecutado ..
Terceiro caso) Reset:= False ; KeepSettings:=True
O Reset sendo False o método passa a olhar para a definição do terceiro parâmetro para definir o comportamento do clone . Como o terceiro parâmetro nesta situação é True, o gene dominante será o KeepSettings . O KeepSettings qnd dominante implica que as propriedades que do clone não serão sobrescritas pelo clientDataSetFonte . Que propriedades seriam essas ? Pela terceira vez, os valores de IndexName (ou IndexFieldNames), filter, filtered, MasterSource, MasterFields, OnFilterRecord e ProviderName .
Pela terceira vez pensaremos na situação levantada acima , onde vc tem clientDataSet com a propriedade ProviderName definida com o nome de algum TDataSetProvider . Imagine ainda que vc definiu no evento BeforeUpdateRecord deste Provider um método inerente a sua necessidade , de modo que quando vc fizer um clientDataSet.ApplyUpdates este evento seja disparado . Como agora o ProviderName do Clone e as demais propriedades (digamos assim , são as originais as que foram previamente definidas) , o tão esperado evento BeforeUpdateRecord será executado
Com isto percebem que para clonar uma fonte de dados teremos então tres possiveis situaçõess
- CloneCds.CloneCursor(CdsFonteDeDados,false,false);//Propriedade do CdsFonteDeDados
- CloneCds.CloneCursor(CdsFonteDeDados,false,True); // Propriedade do CloneCds
- CloneCds.CloneCursor(CdsFonteDeDados,True,false ou True); //Propriedade do CloneCds RESETADAS
Vamos á prática … Utilizando DbExpress (ou outro modo de acesso) crie uma conexão com a sua base de dados preferida . Adicione dois DataSetProvider e altere seu Nomes respectivamente para Provider_Do_Cds_Fonte , Provider_Do_Clone_Cursor . Adicione ao projeto um Button e no Evento Onclick codifique
procedure TForm7.Button1Click(Sender: TObject); var cds_Fonte,Cds_Clone:TclientDataSet; begin cds_Fonte:=TclientDataSet.Create(self); cds_Fonte.providerName:='Provider_Do_Cds_Fonte'; cds_Fonte.Open; Cds_Clone:=TclientDataSet.Create(nil); cds_Clone.ProviderName:='Provider_Do_Clone_Cursor'; Cds_Clone.CloneCursor(cds_Fonte,true,true); //Cds_Clone.CloneCursor(cds_Fonte,true,false); INDIFERENTE > Tanto Faz showmessage(Cds_Clone.ProviderName); //vazio .. Foi Resetado Cds_Clone.Close; cds_Clone.ProviderName:='Provider_Do_Clone_Cursor'; //As´proproedades serão do Cds Fonte de dados Cds_Clone.CloneCursor(cds_Fonte,false,false); showmessage(Cds_Clone.ProviderName); //cds_Fonte de Dados Cds_Clone.Close; cds_Clone.ProviderName:='Provider_Do_Clone_Cursor'; //As proriedades serão do Clone_Cursor como pode se visto abaixo Cds_Clone.CloneCursor(cds_Fonte,false,True); showmessage(Cds_Clone.ProviderName); //cds_Clone_Cursor cds_Fonte.Free; Cds_Clone.Free; end;
A forma que vc irá utilizar este importante método sera inerente á sua necessidade, a intenção aqui foi mostrar possiveis causas de erros e dores de cabeça ao se utilizar o método . Vale ressaltar que o último parâmetro é opcional ( caso não se define ele assumirá KeepSettings=False )
No mais ainda tem alguma particularidades que não foram abordadas , por questões de tempo , paciência e espaço . O que deve ficar claro que é muito bonito usar cloneCursor , mas devemos saber por onde passam as coisas . No mais , estou aberto a qualquer crítica e de prontidão para recebe-la , meu muito obrigado e atê a próxima .