Você está na página 1de 16

Construindo Aplicaes com ClientDataSet e InterBase Express

Construindo Aplicaes com ClientDataSet e InterBase Express


Isto um documento tcnico de uma conversa ocorrida na 12 conferencia anual dos desenvolvedores Borland Por Bill Todd The Database Group, Inc.
Bill Todd presidente da Database Group, Inc.Empresa de consultoria em Banco de dados e Desenvolvimento que fica perto de Phoenix. Ele co-autor de quatro livros de programao de Banco de dados e mais de 80 artigos, e um membro do time que d suporte tcnico no newsgroup da Borland.Ele um orador freqente nas conferencias de desenvolvedores Borland nos EUA e Europa. Bill tambm um conhecido professor e tem diversas classes de programao nos EUA e outros paises.

Introduo Problemas com Interbase Express Usando ClientDataSet Construindo o Data Module Construindo a interface do usurio Alterando os Dados Tratando Erros de Atualizao Ordenando os Dados Mostrando o Status do Registro

Introduo
Agora que o componente ClientDataSet est disponvel nas verses Professional e EnterPrise do Delphi e C++ Builder, hora de olhar as vantagens de usar os ClientDataSets em combinao com os componentes Interbase Express para construir aplicaes Interbase. Como voc ver neste texto existem muitas vantagens em usar ClientDataSets em suas aplicaes. Esta a razo porque o componente ClientDataSet a base da nova arquitetura Dbexpress da Borland. Existem tambm muitas razes pra usar Interbase Express (IBX) para construir aplicaes Interbase. Primeiro, o IBX muito menor e fcil de desenvolver que o BDE. Segundo, o IBX tem acesso mais rpido ao banco de dados Interbase que o BDE. Finalmente, IBX d acesso a caractersticas do Interbase que no esto disponveis pelo BDE tais como mltiplas transaes simultneas, grande variedade de nveis de isolao das transaes e acesso a CommitRetaining e RollbackRetaining.

Problemas com Interbase Express


O maior problema que desenvolvedores encontram trabalhando com os componentes IBX que todo acesso a um banco de dados Interbase deve ser feito dentro do contexto de uma transao. Isto significa que voc no pode sequer ler ou mostrar o contedo de um registro sem iniciar uma transao. O problema que quando voc d commit ou rollback na transao, todos os datasets includos no componente IBTransaction so fechados e o usurio de repente esta olhando uma tela que no mostra nenhum registro. Voc pode ver o efeito no programa IBXDemo que acompanha este texto. A tela seguinte mostra o form principal aps o boto open ter sido clicado. A prxima tela mostra o form aps o click no boto Commit

Construindo Aplicaes com ClientDataSet e InterBase Express

Lgico que existem modos de lidar com isto, e a aplicao IBXDEMO mostrar um deles. Se voc verificar o checkbox Reopen antes de clicar no boto Commit, o programa salva o cdigo da chave primaria das tabelas employee e salary history, confirma (commit) a transao, reabre os dois IBDataSets e posiciona nos registros que estavam antes do commit. A mesma coisa acontece se voc clicar no boto Rollback. Isto funciona, mas um bocado de trabalho extra.

Construindo Aplicaes com ClientDataSet e InterBase Express

Outra soluo seguir a filosofia normal do propsito Cliente/Servidor que iniciar o usurio com uma tela em branco e pedir um critrio de seleo para trazer um pequeno numero de registros para trabalho. O usurio pode ento fazer quaisquer alteraes nos registros e confirmar(commit) as alteraes. Quando as alteraes so confirmadas o usurio estar novamente com uma tela em branco para selecionar outro conjunto de registros. Este acesso requer menos cdigo, mas pode deixar as transaes abertas por um tempo longo demais. Por exemplo, suponha que um usurio precise alterar centenas de registros dos clientes do estado do Paran. Se o usurio selecionou todos os clientes do Paran de uma vez, ele gastar diversas horas de trabalho para fazer tudo. Isso causa dois problemas em potencial. Primeiro, se seu computador travar, todas as alteraes sero desfeitas(rollback) e todo o trabalho ser perdido. Segundo, a transao ficar aberta por muitas horas com centenas de registros alterados e travados e no podero ser alterados por outros usurios. Outra soluo que muitos desenvolvedores usam chamar CommitRetaining ou RollbackRetaining ao invs de Commit ou Rollback. Num primeiro momento isso parece ser a soluo ideal, pois no fecha as tabelas e permite ao usurio continuar a ver e alterar os registros selecionados. Entretanto, CommitRetaining e RollbackRetaining no fecham sua transao. Isso significa que voc est usando um isolamento de transao do momento e voc no pode ver quaisquer alteraes feitas por outros usurios at voc confirmar (commit) ou desistir (Rollback). Tambm significa que voc desabilitou a garbage collection do Interbase forando ele a manter a transao aberta e as informaes dos registros de quando a transao foi iniciada. Isso causa problemas graves de performance em um sistema multiusurio, com muitos usurios alterando diversos registros. Isso nitidamente o caso em que a cura pior que a doena.

Usando ClientDataSet
O componente ClientDataSet um dos componentes da palheta Midas, usado para implementar a estratgia de prover/resolver a edio de registros que foi introduzido como parte do Midas no Delphi 3. Antes de entrar em detalhes de construir uma aplicao que usa os componentes DataSetProvider, ClientDataSet e IBX, vale a pena examinar os fundamentos da estratgia de prover/resolver do Midas.
A estratgia Prover/Resolver

O mecanismo prover/resolve usa um componente DataSetProvider para enviar os dados de um componente Dataset a um ClientDataSet. O componente ClientDataSet guarda os registros recebidos do provedor na memria. Usurios podem inserir, apagar e alterar os registros e as alteraes tambm so guardadas na memria pelo ClientDataSet. Quando o usurio terminar as alteraes, uma chamada ao mtodo ApplyUpdates do ClientDataSet envia as alteraes para o provider. O provider ento gera as instrues SQL necessrias para executar as alteraes no banco de dados (inicia uma transao, envia as instrues SQL para o servidor e, se no houver erros, confirma (commit) a transao). Numa explicao bsica, voc pode ver as vantagens desta arquitetura. Primeiro, as transaes ficam abertas muito pouco tempo. Quando voc abre um ClientDataSet que est conectado a um DataSetProvider, o provider que abre o dataset que est conectado a ele. Quando o dataset aberto, ele executa sua instruo de SQL SELECT e retorna os registros para o provedor que ento os envia para o ClientDataSet fechando o dataset e confirmando (commit) a transao de leitura. Quando voc chama o mtodo ApplyUpdates do ClientDataSet as alteraes so enviadas para o provider que gerar as instrues SQL necessrias, inicia a transao, envia as instrues SQL para o banco de dados e confirma (commit) a transao. Isto faz com que as transaes de leitura e escrita sejam bem rpidas, o que significa que outros usurios no estaro bloqueados por longo tempo. Transaes rpidas tambm significa que o numero de transaes abertas ao mesmo tempo sero menores e o acesso ao banco de dados menor. A segunda grande vantagem desta estratgia te dar o controle da quantidade de dados a serem trabalhados, uma vez que voc controla a instruo SQL que executada, voc controla quantos registros sero trazidos do servidor o que te da o controle sobre o trafego que ser gerado na sua rede e o quanto de memria voc usar para armazenar os registros. Isto ser particularmente valioso se voc estiver desenvolvendo uma aplicao que ser usada numa WAN ou num link de baixa velocidade.

Construindo Aplicaes com ClientDataSet e InterBase Express

Construindo o Data Module

A prxima figura mostra o data module da aplicao de exemplo CDSIBX.

Este data module projetado para suportar o form que mostra a relao um-para-muitos entre a tabela Employee e a tabela Salary_History. A tabela employee selecionada pelo departamento. Por outro lado o dataset Employee inclui um campo lookup no campo Dept_No o qual mostra o nome do departamento. O componente IBDatabase no canto superior esquerdo est configurado exatamente como estaria numa aplicao que somente usa componentes IBX. A seguir vem trs componentes IBTransaction, um para cada tabela usada na aplicao. Quando usar ClientDataSets voc deve usar um componente de transao separado para cada tabela uma vez que diferentes tabelas podem interagir com o banco de dados simultaneamente. A seguir usa-se um componente IBQuery para cada tabela. Verifique que a propriedade Unidirectional de todos os componentes IBQuery est setado como True para reduzir o consumo de memria e aumentar a performance. Uma vez que estes componentes query sero somente usados para trazer o dados da query para o componente DataSetProvider no h necessidade de retornar os dados. Embora o componente IBQuery traga um conjunto de dados read-only, isso ser mais que necessrio uma vez que alteraes no banco de dados sero controladas automaticamente pelo componente DataSetProvider. A propriedade SQL do componente EmpQry ser:
Select * from EMPLOYEE Where Dept_No = :Dept_No order by Last_Name, First_Name

ento os registros de um nico departamento sero selecionados. A instruo SQL para o componente SalHistQry :
select * from SALARY_HISTORY where Emp_No = :Emp_No order by Change_Date desc

Neste caso o parmetro, :Emp_No, tem o mesmo nome que o campo da chave primaria da tabela Employee e a propriedade DataSource do componente SalHistQry esta setada para EmpSrc, o componente DataSource que esta conectado ao componente EmpQry. Isto determina ao componente SalHistQry para pegar o valor do parmetro do registro corrente de EmpQry. Isto traz somente os

Construindo Aplicaes com ClientDataSet e InterBase Express

registros do histrico de salrios dos empregados que esta selecionado. A propriedade SQL do componente DeptQry e:
select DEPT_NO, DEPARTMENT from DEPARTMENT order by Dept_No

o qual seleciona todos os nomes e nmeros dos departamento. Um nico componente DataSetProvider o provider do componente EmpQry. A propriedade Dataset do provider esta setada para EmpQry. Nenhum provider necessrio para o componente SalHistQry porque seus registros sero includos num campos do dataset nos registros trazidos pelo DataSetProvider do EmpQry. Isto acontece automaticamente para qualquer componente filho que ligado a um componente pai atravs da propriedade Datasource. Um segundo componente DataSetProvider ligado ao componente DeptQry para trazer os registros do departamento. Finalmente trs componentes ClientdataSet sero usados para fornecer os dados para a interface do usurio da aplicao. O primeiro EmpCds conectado ao provider EmpProv, definindo a propriedade ProviderName do ClientDataSet para EmpProv. Se voc est usando ClientDataSet em uma aplicao Multitier verifique se a propriedade RemoteServer ser deixada em branco, uma vez que o provider esta na mesma aplicao como o ClientDataSet. Aps conectar ao ClientdataSet EmpCds ao seu provider, o prximo passo abrir o Editor de Campos (fields Editor) e inserir todos os campos como mostrado abaixo.

Observe o ultimo campo SalHistQry. Este um campo cujo tipo TdataSetField e ele contem o registro do histrico de salrios devolvido pelo dataset SalHistQry. Voc deve instanciar o campo no editor de campos para o campo ficar disponvel, ou voc no ter acesso aos registros do histrico de salrios. Agora voc pode incluir o componente SalHistCds e definir sua propriedade DataSetField para SalHistQry usando o campo drop-down. Isto conecta o Clientdataset SalHistCds ao campo SalHistQry no dataset employee. O terceiro Clientdataset DeptCDs est conectado ao DatasetProvider DeptProv pela propriedade Providername. Finalmente dois componentes Datasource so conectados aos clientdataset EmpCds e SalHistCds.

Construindo Aplicaes com ClientDataSet e InterBase Express

Construindo a interface do usurio

Uma vez que estamos interessados nos componentes de acesso aos dados, a interface do usurio no exemplo CDSIBX no nada especial. A tela principal neste ponto mostrada abaixo:

O form contem dois grids e dois DBNavigators para mostrar os registros dos empregados e do histrico de salrios respectivamente. Para fazer esta aplicao parecer uma cliente/servidor normal, onde o usurio somente trabalha com um subconjunto dos dados, existe um combo box que mostra os departamentos. Selecionando um departamento no combo box, automaticamente seleciona os empregados daquele departamento usando o cdigo no evento OnChange do combo box, como mostrado abaixo.
procedure TMainForm.DeptComboChange(Sender: TObject); begin with MainDm do begin EmpCds.Close; EmpCds.Params.ParamByName('Dept_No').AsString := Copy(DeptCombo.Items[DeptCombo.ItemIndex], 1, 3); EmpCds.Open; SalHistCds.Open; end; //with end;

Este cdigo fecha o Clientdataset EmpCds, coloca os trs primeiros caracteres do item selecionado no combo box no parmetro Depto_No do EmpCds e reabre EmpCds. A propriedade Params do Clientdataset uma lista do objeto TParam e cada objeto TParam representa um dos parmetros na instruo SQL do componente dataset que o DataSetProvider do ClientDataSet est ligado, neste caso EmpQry. Isso Significa que voc pode facilmente alterar o(s) valor(es) da instruo SQL e trazer um novo grupo de registros a qualquer tempo. Se voc quer mais flexibilidade, voc pode usar a propriedade CommandText do ClientDataSet para alterar toda a instruo SQL. Suponha que voc queira deixar sua aplicao apta a selecionar os empregados tanto por departamento ou por grade de salrios. A aplicao Exemplo tem uma opo de

Construindo Aplicaes com ClientDataSet e InterBase Express

seleo do menu principal que oferece estas duas escolhas. O form realmente contem dois painis posicionados no topo de cada um. Um tem o combo box para escolha do departamento e o outro tem um Edit Box para permitir ao usurio digitar a grade. Quando a aplicao inicia, o painel do departamento est visvel e o painel de grades no. Aqui o cdigo para o evento OnExecute do item EmpByGrade anexado ao item de menu Employee By Grade.
procedure TMainForm.EmpByGradeExecute(Sender: TObject); begin DeptPanel.Visible := False; GradePanel.Visible := True; with MainDm.EmpCds do begin Close; with MainDm.EmpQry.SQL do begin Clear; Add('SELECT * FROM EMPLOYEE WHERE JOB_GRADE = :JOB_GRADE ORDER BY LAST_NAME, FIRST_NAME'); end; //with Params.Clear; FetchParams; GradeEdit.SetFocus; end; //with end;

Este evento inicia fazendo o painel do departamento invisvel e o painel de grade visvel. A seguir ele fecha o Clientdataset EmpCds, limpa a propriedade SQL do IBQuery e insere uma nova instruo na propriedade SQL. Finalmente o cdigo limpa a propriedade Params para remover o parmetro Dept_No e chama o mtodo FetchParams do ClientDataSet's para criar o parmetro da nova instruo SQL. Para selecionar os empregados pela grade apenas digite um numero de grade no edit box e tecle Enter. O cdigo seguinte do evento OnKeyDown do TEdit fecha o ClientDataSet, seta a grade ao parmetro Job_Grade e reabre o ClientDataSet.
procedure TMainForm.GradeEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var Grade: Integer; begin if Key = VK_RETURN then begin with MainDm.EmpCds do begin try Grade := StrToInt(GradeEdit.Text); except Grade := 0; end; //try Close; Params.ParamByName('Job_Grade').AsInteger := Grade; Open; MainDm.SalHistCds.Open; end; //with end; //if end;

Alterando os dados

Quando voc insere, apaga e altera registros, suas alteraes so armazenadas na propriedade Delta do ClientDataSet. Para enviar estas alteraes para o banco de dados voc deve chamar o mtodo ApplyUpdates do ClientDataSet. At voc chamar o evento apply updates voc pode desfazer as alteraes em ordem reversa, desfazer todas as alteraes do registro corrente ou desfazer todas as alteraes de todos os registros. O menu Edit na aplicao de Exemplo dar todas estas escolhas. O cdigo do evento OnExecute dos itens associados a cada item do menu mostrada abaixo.

Construindo Aplicaes com ClientDataSet e InterBase Express

procedure TMainForm.UndoCurrentRecordExecute(Sender: TObject); {Desfaz todas as alteraes no registro corrente.} begin MainDm.EmpCds.RevertRecord; end; procedure TMainForm.UndoLastChangeExecute(Sender: TObject); {Desfaz a ultima alterao e move-se p/ o registro alterado.} begin MainDm.EmpCds.UndoLastChange(True); end; procedure TMainForm.CancelChangesExecute(Sender: TObject); {Cancela todas as alteraes.} begin MainDm.EmpCds.CancelUpdates; end; procedure TMainForm.ApplyUpdatesExecute(Sender: TObject); {Se existirem alteraes no executadas na propriedade Delta ento execute-as.} begin with MainDm.EmpCds do begin if ChangeCount > 0 then begin ApplyUpdates(-1); Refresh; end; //if end; //with end;

O mtodo UndoLastChange, mostrado no terceiro evento acima, pega um parmetro boleano. Se o parmetro true a ultima alterao ser desfeita e o registro se tornar o registro corrente a menos que exista um registro inserido mais novo. Se o parmetro false a ultima alterao desfeita, mas o registro corrente no muda, a menos que a ultima alterao tenha feito o registro corrente desaparecer por ser uma insero e ter sido a ultima alterao. O ultimo evento acima chama o mtodo ApplyUpdates. Antes de chamar o mtodo ApplyUpdates o cdigo verifica se a propriedade ChangeCount maior que zero. ChangeCount retorna o numero de alteraes no confirmadas na propriedade Delta. Se ChangeCount for zero, porque no existem alteraes a serem confirmadas ento no h razo para executar o mtodo ApplyUpdates. ApplyUpdates usa um nico parmetro que o numero de erros permitidos antes de abortar o processo. Suponha que existem dez registros esperando para serem confirmados e voc chama o mtodo ApplyUpdates com o parmetro MaxErros igual a um. Se o segundo registro causar um erro o processo ir parar e no haver meios de tentar confirmar as outras oito alteraes no banco de dados. Se o parmetros MaxErrors for setado para menos um no haver limites para o numero de erros permitidos. Toda alterao tentar ser confirmada mesmo quer todas causem erros. ApplyUpdates previne alteraes em registro que foram alteradas por outros usurios desde o momento em que voc leu o registros incluindo a clausula WHERE em todas as instrues de INSERT, DELETE e UPDATE geradas pelo DataSetProvider. A propriedade UpdateMode do controle DataSetProvider cujos campos foram includos na clausula WHERE. UpdateMode pode ser setada para: 1. upWhereAll (default) - Se UpdateMode for setada para upWhereAll todos os campos que no forem BLOB do registro sero includos na clausula WHERE. Isto significa que voc obter um erro quando voc confirmar suas alteraes se outro usurio alterou qualquer campos do registro mesmo se o campo que o outro alterou no seja o campo que voc alterou. 2. upWhereChanged - Usando upWhereChanged inclui somente os campos que voc alterou na clausula WHERE, ento voc somente obter um erro se outro usurio alterou os campos que voc est alterando. Esta a configurao que provavelmente voc usar na maioria dos casos. 3. upWhereKeyOnly - A opo upWhereKeyOnly inclui somente os campos da chave primria (primary key) na clausula WHERE. Isto significa que se outro usurio alterar um campo e voc alterar o

Construindo Aplicaes com ClientDataSet e InterBase Express

mesmo campo, suas alteraes sobreporo as alteraes do outro usurio. Existe porem uma limitao. A clausula WHERE nunca inclui campos BLOB, ento se outro usurio alterar o registro desde o memento que voc o leu e somente alterou o campo BLOB, voc no ser avisado quando atualizar o registro. Alterando Datasets que no podem ser alteradas Trabalhar com registro retornados por uma stored procedure no servidor tem um principal problema. Voc no pode atualizar os registros. Todavia, voc pode alterar Datasets de uma stored procedure quando voc usa ClientDataSet e DataSetProvider. Isto fcil porque o dataSet Provider j gera as instrues SQL para executar as atualizaes. O nico problema os registros vem de uma Stored Procedure e o provider no tem meios de achar o nome da tabela que deveria ser atualizada. Felizmente, o DataSetProvider tem um evento OnGetTableName que dispara quando gerado a expresso SQL para solicitar atualizaes. Tudo o que voc tem a fazer escrever uma linha de event handler que determina o nome da tabela para atualizar para os eventos de parmetro TableName e voc poder agora solicitar atualizaes para registros retornados por uma a stored procedure. Essa tcnica tambm til para instrues SQL SELECT que poderiam no ser normalmente atualizveis. Suponha que voc tenha uma multi-table join query que seja necessrio ao usurio poder ver informaes de duas ou mais tabelas, mas o usurio s precisa fazer mudanas para a rea de uma tabela. Tudo o que precisa fazer instanciar os objetos do campo no tempo planejado para o ClientDataSet e usar a propriedade ReadOnly para todos os campos que o usurio no pode mudar. Ento cria-se um evento OnGetTableName para o DataSetProvider fornecer o nome da tabela em que as atualizaes deveriam ser aplicadas. A vida no to fcil se voc tem uma join multi-table e usurios precisam mudar registros em duas ou mais tabelas. Nesse caso voc vai precisar escrever um evento BeforeUpdateRecord para o componente DataSetProvider A propriedade Delta do ClientDataSet transmitida para esse evento em propriedade DeltaDs O tipo de propriedade do DeltaDs TClientDataSet ento, voc precisa acessar a todas as propriedades e mtodos do componente ClientDataSet em seu evento. Nesse evento voc precisar transferir os registros da propriedade do DataSet e gerar suas prprias instrues SQL para aplicar as atualizaes. Voc pode usar o mtodo UpdateKind para descobrir se o processo de registro pode ser inserido, apagado ou modificado. Os objetos de campo do parmetro DelltaDs tm um OldValue e uma propriedade NewValue. Isso permite a voc usar DeltaDs.FieldByName('SomeField').OldValue para obter o velho valor do campo e DeltaDs.FieldByName('SomeField').NewValue para obter um novo valor de campo. Obviamente registros para serem deletados tm somente velhos valores e registros a serem inseridos tm somente novos valores. Registros modificados so cuidados de uma forma no intuitiva, mas faz sentido uma vez que voc o compreende. Para cada registro que modificado existem dois registros adjacentes no DataSet. O primeiro registro tem um status atualizado para usUnmodified e o registro como foi dito anteriormente, nenhuma mudana foi feita . O segundo registro de status atualizado para usModified e contm valores para os campos que foram mudados apenas. As propriedades de OldValue e NewValue dos objetos de campo so variveis Quando voc examina o NewValue para um campo de registro usModified ele ser: Unassigned se o valor do campo no for alterado, Null se o valor do campo for mudado para algo sem validade e ter um novo valor se o campo for alterado para nulo ou qualquer valor para outro valor. Voc pode usar as funes VasIsEmpty e VarIsNull para ver se um campo foi modificado ou modificado para nulo. Usando essa informao voc pode gerar suas prprias instrues SQL INSERT, DELETE e UPDATE para cada tabela que tiver que atualizar, determinar as instrues SQL para propriedades SQL de um componente IBSQL e executa-lo. Enquanto voc processa cada registro, altere o parmetro Applied para True ento o provider saber para no executar as atualizaes.
Tratando erros de atualizao

Quando voc chama o ApplyUpdates a propriedade Delta enviada para o DataSetProvider que gera instrues SQL para aplicar cada modificao para o database e executa-lo. Se um erro ocorrer o registro salvo. Quando todas as atualizaes forem tentadas ou quando a conta de erro mximo permitido for alcanada, ou o que vier primeiro, nenhum erro ser retornado para o ClientDataSet que dispara um evento OnReconcileError. Ento, voc deve fornecer um evento OnReconcileError para distribuir com algum erro que ocorra. Felizmente, engenheiros do Borland fazem isso fcil. Se voc

Construindo Aplicaes com ClientDataSet e InterBase Express

selecionar File | New do menu Delphi e ir para a pgina de Dialogs do repositrio voc ir encontrar um form chamada Reconcile Error Dialog. Tudo o que precisa fazer adicionar essa form para sua aplicao, adicionar essa unit na clusula uses de cada unit que contm um ClientDataSet e adicionar o cdigo mostrado abaixo para cada evento ClientDataSet's OnReconcileError.
procedure TMainDm.EmpCdsReconcileError(DataSet: TClientDataSet; E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction); begin Action := HandleReconcileError(DataSet, UpdateKind, E); end;

Quando um erro ocorre enquanto esto sendo aplicadas as atualizaes esse cdigo usa uma caixa de dilogo para exibir o que mostra o registro, as modificaes feitas no registro, o tipo de atualizao tentada (insert, delete or modify) e a mensagem de erro. O usurio pode editar esse registro e optar por uma das seis aes seguintes A ao selecionada determinada por um evento Action parameter. 1. 2. 3. 4. 5. 6. raSkip No aplica atualizaes mas deixa as modificaes na propriedade Delta. raAbort Anula toda a operao de atualizao. Nenhuma modificao ser aplicada. raMerge - Atualiza o registro mesmo que tenha sido alterada por outro usurio. raCorrect Troca as modificaes no Delta com as alteraes feitas por outro usurio. raCancel Desfaz todas as modificaes do registro. raRefresh Desfaz todas as modificaes do registro e rel o registro do banco de dados.

Voc pode modificar a aparncia ou atributo do dilogo de reconciliao de erros do repositrio ou escrever seu prprio evento OnReconcileError do zero, que atenda suas necessidades.
Sorting Data On-the-Fly

O fato de que o ClientDataSet guardar os registros na memria em sua propriedade Data uma grande vantagem. Um exemplo disso que voc pode ordenar os registros por qualquer campo ou combinao de campos simplesmente designando o nome do campo para a propriedade IndexFieldNames do ClientDataSet. Para ordenar por mais de um campo separe os nomes dos campos com ponto e vrgula. O cdigo seguinte o evento OnTitleClick para o DBGrind que mostra os registros dos empregados.
procedure TMainForm.EmpGridTitleClick(Column: TColumn); begin MainDm.EmpCds.IndexFieldNames := Column.FieldName; end;

Esta nica linha do cdigo permite ao usurio clicar no ttulo de qualquer coluna para ordenar os registros empregados dessa coluna. Essa tcnica tem uma restrio e duas limitaes. A restrio que voc no pode usar IndexFieldNames em um campo calculado do dataset, como os registros do histrico dos salrios na aplicao exemplo. De fato, no h maneira de mudar a ordenao de um campo calculado no dataset exceto mudando a clusula ORDER BY da query. A primeira limitao que a ordenao sempre ascendente. No h maneira de especificar uma ordenao descendente usando propriedades IndexFieldNames. Segundo, se voc tiver um grande nmero de registros no ClientDataSet, por exemplo, 50,000, a ordenao pode levar alguns segundos( ou minutos). Ambas as limitaes podem ser facilmente superadas usando indexes. Voc pode criar indexes em um ClientDataSet em tempo de projeto usando a propriedade IndexDefs ou em runtime usando o mtodo AddIndex. Ambos os mtodos permitem a voc criar indexes ordenados ascendentemente por alguns campos e descendentemente por outros bem como os indexes que so case insensitive em um ou mais campos. Por todos os registros estarem na memria criar um index muito rpido. Por exemplo, criar um index de 50,000 registros leva s alguns segundos. Criar um index de 1,000 registros basicamente instantneo. A aplicao de exemplo inclui um index criado em tempo de projeto que ordena os registros empregados descendentemente quanto pelo salrio e ascendentemente pelo nome. A figura seguinte mostra o IndexDef para o index no Object Inspector e o cdigo seguinte a figura do Object Inspector mostra como voc pode criar o mesmo index usando o mtodo AddIndex do ClientDataSet.

Construindo Aplicaes com ClientDataSet e InterBase Express

MainDm.EmpCds.AddIndex('DescBySalary', 'Salary;Last_Name;First_Name', [ixDescending], 'Salary');

Nessa chamada ao AddIndex o primeiro parmetro o nome do index, o segundo a lista do nome dos campos a serem indexados, a terceira o parmetro de opes do index e a quarta so os nomes dos campos a serem ordenados em ordem descendente. O mtodo AddIndex do ClientDataSet pode ter dois parmetros adicionais que no sero usados nesse exemplo. O primeiro a lista de campos que sero usados em casos insensitivos e o segundo em nvel de ordem de grupo. Nvel de ordem do grupo usado como campo agregado para acumular subtotais. Campos agregados sero descritos mais tarde. O cdigo seguinte o evento OnExecute da ao DescendingBySalary que chamada pelo item de meu Descending By Salary.
procedure TMainForm.DescendingBySalaryExecute(Sender: TObject); begin if DescendingBySalary.Checked then begin DescendingBySalary.Checked := False; MainDm.EmpCds.IndexFieldNames := gByNameFieldNames; end else begin DescendingBySalary.Checked := True; MainDm.EmpCds.IndexName := gDescBySalaryIndex; end; //if end;

Essa ao troca entre o index DescBySalary e a ordenao por nome usando a propriedade IndexFieldNames. O evento OnClick do item Sort1 do menu, mostrado abaixo, verifica o valor corrente da propriedade EmpCds.IndexName e seta a propriedade Checked do item de ao em caso da ordenao ter sido modificada por um click na barra de ttulo do grid.
procedure TMainForm.Sort1Click(Sender: TObject); begin if MainDm.EmpCds.IndexName = 'DescBySalary' then DescendingBySalary.Checked := True else DescendingBySalary.Checked := False;

Mostrando o status do registro

Construindo Aplicaes com ClientDataSet e InterBase Express

Voc pode facilmente mostrar o status de um registro, isso , se tiver sido modificado, inserido ou no modificado, usando um campo calculado. O campo de status do ClientDataSet Employee um campo calculado e o cdigo mostrado um evento OnCalcFields.
procedure TMainDm.EmpCdsCalcFields(DataSet: TDataSet); begin case EmpCds.UpdateStatus of usModified: EmpCdsStatus.AsString := 'Modified'; usUnModified: EmpCdsStatus.AsString := 'Unmodified'; usInserted: EmpCdsStatus.AsString := 'Inserted'; usDeleted: EmpCdsStatus.AsString := 'Deleted'; end; end;

O Chamado ao mtodo UpdateStatus do ClientDataSet retorna a uma das quatro constantes, usModified, usUnmodified, usInserted ou usDeleted. Claro que voc nunca ver um registro cujo status usDeleted.
Filtrando Dados

Uma maneira do clientdataset ajudar voc a reduzir o trafego da rede e a carga do servidor filtrando os registros em um ClientDataSet ao invs de executar outro query. Por exemplo, suponha um usurio selecione todos os registros do cliente de um estado e voc precise ver os registros de somente as vendas de um territrio naquele estado. Voc pode exibir somente os registros de um territrio em uma das trs maneiras. Primeiro, se voc tem um edis do campo do territrio e ele o index ativo que voc pode usar o mtodo SetRange do ClientDataSet mostrado abaixo.
CustomerCds.SetRange([23], [23]);

Esse mtodo de chamada vai restringir sua viso dos dados para somente aqueles registros no territrio 23. Para remover a seleo e ver todos os registros chama-se o mtodo CancelRange. A segunda tcnica aplicar um filtro usando a propriedade filter do ClientDataSet. Os filtros ClientDataSet usam sintaxe SQL WHERE e so, ento mais poderosos que os filtros BDE que voc deve ter usado no passado. O cdigo seguinte mostraria os registros para o territrio 23 usando um filtro.
with CustomerCds do begin Filter := 'Territory = 23'; Filtered := True; end;

Para remover o filtro coloque a propriedade Filtered para False. A terceira maneira de filtrar registros escrever um evento OnFilterRecord.
Usando tabelas de lookup Locais.

Outra forma de reduzir o trfico de rede e a carga do servidor usar ClientDataSets para todos lookup tables que voc pode usar p/ validao dos dados, Combo Boxes e para suportar campos de lookup no ClientDataSets. Para tabelas com poucos milhares de registros ou menos voc tem que selecionar todos os registros de um servidor. Ento, enquanto voc no chama o mtodo Refresh do ClientDataSets, sua aplicao usar os registros guardados na memria da propriedade Data do ClientDataSets e nunca reler esses registros do servidor.
Campo agregados.

Campo agregados permite que voc defina valores de sumario que sero automaticamente mantidos pelo ClientDataSet. Para criar um campo agregado use o Fields Editor para adicionar um novo campo, cujo tipo aggregate, no ClientDataSet como mostrado abaixo.

Construindo Aplicaes com ClientDataSet e InterBase Express

Campo agregados so mostrados numa seo separada do Fields Editor abaixo dos outros campos como mostrado abaixo.

Quando selecionado o campo Total_Salary que um campo agregado, aparecer como mostrado abaixo no Object Inspector.

Construindo Aplicaes com ClientDataSet e InterBase Express

Para usar o campo agregado voc deve definir a propriedade Active p/ True e digitar uma expresso que descreve o valor que voc quer na propriedade Epression. Voc deve definir tambm a propriedade AggregatesActive do ClientDataSet p/ True. Neste exemplo a expresso uma simples soma (sum(salary)), que calcula o total de salrios de todos os registros dos empregados no ClientDataSet. Voc pode tambm usar as funes Min, Max, Avg e Count. E Pode tambm construir expresses complexas. Por exemplo, o campo SalHistCds do ClientDataSet tem um campo agregado cuja expresso : sum(New_Salary) - sum(Old_Salary) para calcular o total dos salrios alterados do empregado selecionado. O mesmo resultado pode ser obtido com a expresso Sum(New_Salary - Old_Salary). As duas maiores restries para as expresses campo agregado so que voc no pode combinar funes, ento Avg(Sum(New_Salary - Old_Salary)) no certo e voc no pode usar valores de campos non-aggregated na expresso porque no existe meio de determinar de qual registro veio o valor. Voc pode tambm calcular agregados para grupo de registros. Por exemplo, voc pode ter subtotais e medias por grupo. A prxima tela da aplicao exemplo mostra o form que aberto quando voc seleciona o item View| Employee Statistics no menu. Verifique que as trs colunas da direita do grid mostram o total de salrios, a media dos salrios e o numero de empregados por departamento.

O primeiro passo para criar um campo agregado que usa grupos criar um ndice que agrupe os registros que voc deseja. Na aplicao exemplo um ndice para os campos Dept_No, Last_Name e First_Name foi definido na propriedade IndexDefs do ClientDataset EmpStatsCds. A propriedade IndexName do ClientdataSet foi setada para este ndice que foi nomeado AscByDept. Alem disso a propriedade GroupingLevel do ndice foi setada para 1 para indicar que somente queremos agrupar pelo primeiro campo do ndice. Ele poderia ser setado para um valor maior at o numero de campos do ndice. Criar campos agregados que usa grupos o mesmo que criar um que no usa exceto que voc deve definir duas propriedades adicionais. A primeira a propriedade GroupingLevel do campo agregado que deve ser setado para o numero de campos que voc que agrupar. Neste exemplo nos agrupamos pelo primeiro campo do ndice que Depto_No, ento GroupingLevel foi definido como um. A segunda propriedade IndexName. Neste exemplo a propriedade IndexName do campo agregado foi definida com AscByDept. O campo agregado somente ser calculado quando este ndice for o ndice ativo do ClientDataSet.

Construindo Aplicaes com ClientDataSet e InterBase Express

Clonando Datasets

Outra caracterstica bastante til do ClientDataSet a habilidade de Clonar Cursores. Suponha que voc precise ver dois diferentes registros do mesmo Dataset ao mesmo tempo. Se voc no est trabalhando com ClientDataSets voc tem de procurar o registro e salvar ele num array ou usar um outro componente Query para buscar o registro, o que cria trafego e carrega o servidor. Se voc est usando ClientDataSets e precisa de uma viso adicional de seus dados, tudo o que voc precisa fazer incluir outro ClientDataSet na sua aplicao e chamar o mtodo CloneCursor como mostrado no seguinte cdigo no evento OnCreate do form EmpCloneForm na aplicao exemplo.
procedure TEmpCloneForm.FormCreate(Sender: TObject); begin with EmpCloneCds do begin CloneCursor(MainDm.EmpCds, False, False); Open; end; //with end;

CloneCursor recebe trs parmetros. O Primeiro o ClientDataSet origem, o segundo e o terceiro so chamados Reset e KeepSettings e so ambos booleanos. Quando Reset e KeepSettings so false as propriedades RemoteServer, ProviderName, IndexName, Filter, Filtered, MasterSource, MasterFields e ReadOnly so copiadas do ClientDataSet origem para o Clone. Quando Reset True as propriedades do close so setadas para seus valores Default. Quando KeepSettings True a propriedade values do close no alterada.
Salvando Dados Localmente

O componente ClientDataSet tem os mtodos SaveToFile e LoadFromFile. SaveTofile salva o contedo da propriedade Data do ClientDataSet e qualquer ndice que foi definido em tempo de projeto na propriedade IndexDefs para um arquivo local num formato proprietrio. Se a extenso for XML somente os dados sero salvos no formato XML. LoadFromFile carrega os dados salvos de volta ao ClientDataSet. A parte de ser muito fcil converter os dados para XML, estas funes tem muitos outros usos. Uma criar aplicaes de sincronismo que pode baixar um subset de registro do banco de dados no servidor, salvar os dados localmente, desconectar da rede, Incluir, apagar e alterar registros, reconectar a rede e gravar as alteraes no banco de dados do servidor. Voc pode tambm usar esta habilidade para reduzir o trafego em aplicaes que rodam em redes lentas e que usa tabelas que so relativamente estticas e contem moderado numero de registros. Apenas leia os dados das tabelas estticas no ClientDataSet e salve eles localmente. A partir da uses os dados das tabelas locais. Tudo o que voc precisa uma tabela de controle no banco de dados atualizada por uma trigger que mostre a data e hora que cada tabela foi alterada. Quando sua aplicao inicia, verifique a data e hora da ultima atualizao de cada tabela e somente baixe do servidor se a tabela foi alterada desde a ultima vez que sua aplicao as carregou. Voc pode tambm usar o ClientDataSet como um banco de dados desktop, sem utilizar banco de dados externos ou servidores.
Usando ClientDataSets como tabelas em memria

Voc pode usar o ClientdataSet sem conectar a um Provider. No design inclua os campos na propriedade FieldDefs usando o editor de propriedades. Ento tudo o que voc tem a fazer clicar com o boto direito no ClientdataSet e selecionar Create Dataset no menu. Voc agora tem uma tabela em memria com todas a caractersticas descritas acima. Voc tambm pode incluir o FieldDefs em runtime usando o mtodo FieldDefs.Add e chamar o mtodo CreateDataSet do ClientDataSet para criar tabelas temporrias. Use estas tabelas temporrias para manipulao de dados, relatrios ou qualquer outra situao onde voc deseja aumentar a performance e reduzir o trafego da rede armazenando os dados na memria.

Construindo Aplicaes com ClientDataSet e InterBase Express

Usando ClientDataSet

Se voc usar ClientedataSets em sua aplicao voc deve distribuir a MIDAS.DLL com sua aplicao. Esta DLL contem o cdigo para suporte ao ClientDataSet. Voc deve colocar a MIDAS.DLL no diretrio da sua aplicao, no diretrio Windows/System, no diretrio Windows ou qualquer diretrio que esteja no PATH.

Sumrio.
Usar ClientDataSets em suas aplicaes Interbase oferece muitos benefcios. ClientDataSets pode reduzir tanto a carga do servidor quanto o trafego na rede. Ele tambm melhora a concorrncia, pela diminuio do tempo que as transaes ficam abertas. Somando a isso permite voc ordenar e filtrar seus dados rpida e facilmente, permitindo fazer modelo de aplicaes com sincronismo de dados e faz queries e Stored Procedures atualizveis. Finalmente voc pode usar ClientDataSets como tabelas temporrias e/ou tabelas na memria.
Copyright 1994 - 2002 Borland Software Corporation. All rights reserved. Legal Notices Privacy Policy

Artigo Original: Por Bill Todd The Database Group, Inc.

Traduo e adaptao: Cludio Srgio Gonalves claudio@clarosistemas.com.br

Comunidade Firebird de Lngua Portuguesa Visite a Comunidade em: http://www.comunidade-firebird.org