Array
(
)

Chave primária com ClientDataSet(CDS)

Lero
   - 22 nov 2005

Olá pessoal,
Estava com um problema num projeto da facul, e já estava pra postar aqui, mas agora a coisa ficou pior pq surgiu o mesmo problema no trampo. Preciso de uma rotina segura para a manipulação das chaves primárias no meu banco. Já dei uma pesquisada e o melhor q eu achei foi fazer uma função recursiva q dá um applyupdates com um novo valor de chave qdo dá erro. Porém, não acho q seria o ideal, pois no help eu verifiquei q existe o evento on UpdateError para manipular esse tipo de problema, e me pareceu a melhor solução. Só q eu não entendi como se utiliza esse evento. Se alguem puder me dar uma :idea: eu ficarei muito grato.
Ah... O meu trabalho da facul é um desafio um pouco maior. É um mestre-detalhe. Aí a minha dúvida é a seguinte: Eu posso manipular só a chave do CDS mestre, ou eu preciso fazer isso com o CDS detalhe também.

Muitoi obrigado pela ajuda.
Leomar de Rossi Ferreira


Emerson
   - 22 nov 2005

uma pergunta: sua chave-primária é um campo auto-incremental?


Lero
   - 22 nov 2005

Não, é um integer comum mesmo. Eu não gosto de trabalhar com chave auto-incremental, pq não me permite ter controle pleno dos dados.

Leomar de Rossi Ferreira.


Lero
   - 22 nov 2005

Tentei fazer com o seguinte código no UpdateError do datasetprovider:

#Código

begin
cdsAgendaagenda_id.NewValue := StrToInt(VarToStr(cdsAgendaagenda_id.Value)) + 1;
Response := rrApply;
end;


O problema é q qdo ele vai setar o NewValue, ele dá um erro de ´Falha Catastrófica´. Como faço para setar a NewValue corretamente? Acho q se conseguir setar o problema tá resolvido. Qualquer ajuda serve, por favor.

Leomar de Rossi Ferreira


Emerson
   - 22 nov 2005

sugiro que você faça essa avaliacão no evento OnUpdateRecord do provider. verifique o tipo de atualização (ukInsert) e preencha ali o seu campo-chave.


Lero
   - 22 nov 2005

Mas e se tiverem processos concorrentes? Utilizando o UpdateRecord não existe a possibilidade de dois ou mais usuários pegarem o mesmo valor ao mesmo tempo? A minha idéia era ´insistir´ com o BD até q o DataSetProvider coloque um valor q não viole a chave?

Ahhh... Eu tô dando o ApplyUpdates dentro de uma transação, claro. A transação impediria a colocação de duas chaves com o mesmo valor ao mesmo tempo? Quero dizer, se eu indicar o valor no UpdateRecord e o DSP aplicar o insert sem erro, outro cliente q busque a chave após o insert, mas durante a transação antes do commit vai ter retornado qual valor? De qualquer forma ainda poderia haver erro, pois o outro cliente pode pegar o valor antes do insert, mas é uma dúvida q eu tenho há algum tempo.

Leomar de Rossi Ferreira


Emerson
   - 22 nov 2005

um valor chave não deve ser informado no momento do insert. a menos que esse valor possa ser desprezado.
por exemplo:
usuario A inseriu. codigo 1;
usuario B inseriu. codigo 2;
usuario B gravou; codigo 2;
usuario A cancelou;
dessa forma pode acontecer de ficarem ´buracos´ entre os códigos.


se a atribuição for feita somente no momento da gravação (o ukInsert do OnUpdateRecord só acontece no momento do ApplyUpdates, porque o provider só reconhece a inserção no momento em que ela for descarregada para o HD)
usuario A inseriu.
usuario B inseriu.
usuario C inseriu.
usuario B gravou; codigo 1;
usuario A cancelou;
usuario C gravou; codigo 2;


Lero
   - 23 nov 2005

Emerson,
Acho q não entendi direito. No exemplo q vc deu se o usuário A cancela a gravação e o B grava, a chave do registro do B seria 1, mas se o A gravasse, a chave seria 2 para o B. O q eu não entendi é como eu faço pro cliente receber um valor de chave único sendo q ainda não foi gravado no banco. Eu não sei como se faz isso, se vc puder me dar uma orientação, eu agradeço.
Ah... Eu não achei o evento onUpdateRecord, no DataSetProvider só tem BeforeUptdateRecord e AfterUpdateRecord. No CDS tb não tem, nem no ADODataSet q está ligado ao provider. Em qual componente ele fica? (o meu Delphi é o 7)

De qualquer forma, acho q o ideal pra mim seria utilizar o OnUpdateError, só q qdo eu tento alterar o TField.NewValue ele dá erro de ´Falha Catastrófica´ :shock: . Tô achando q esse problema é por causa da mistura explosiva ADO + DSP + CDS. Alguém já teve esse problema?


Emerson
   - 23 nov 2005

me desculpe. o evento é BeforeUpdateRecord do DatasetProvider.

agora, para poder te ajudar de forma satisfatória, como você adquire o valor da chave? pega o último valor e soma um, ou tem uma tabela que controla a numeração?


Lero
   - 23 nov 2005

Pego o maior valor e somo 1. Mas vou fazer uma rotina pra pegar o menor valor disponível. Acho q vou criar uma function no próprio banco de dados pra isso.


Emerson
   - 23 nov 2005

tenha no seu Datamodule uma query para uso generico. eu sempre crio uma com o nome de dataset_geral. eis um exemplo de uso com sql para buscar o último codigo.

#Código

procedure TForm1.DataSetProvider1BeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind; var Applied: Boolean);
var
proximocodigo: integer;
begin
case UpdateKind of
ukInsert:
if SourceDS = SeuADODataset then
begin
// pega o próximo codigo
with ADODataset_geral do
begin
close;
commandtext := ´select max(codigo) from tabela [where...]´;
open;
if fields[0].isnull
then proximocodigo := 1
else proximocodigo := fields[0].asinteger + 1;
close;
end;
DeltaDS.FieldByName(´codigo´).newvalue := proximocodigo;
end;
end;
end;


adicione a opção poPropagateChanges na propriedade Options do Provider para que o novo código seja exibido no seu dataware após a gravação.


Lero
   - 24 nov 2005

Blz. Valew mesmo por toda a ajuda, Emerson. Fiz do jeito q vc falou, no BeforeUpdateRecord e tá funcionando legal, só falta fazer a function no banco pra evitar os intervalos entre os valores. Aproveitei pra implementar essa técnica no meu trabalho da facul q tem master-detail e ficou perfeito.
Mais uma vez, muito obrigado pela sua colaboração.

Leomar de Rossi Ferreira


Emerson
   - 24 nov 2005

amigo, da forma que eu te passei não ficam intervalos entre os valores:
- os valores serão atribuídos somente na execução do applyupdates.
- só haverá intervalos se por acaso vc estiver no código 5 e algum usuário excluir o registro 3. obviamente o próximo será o 6 e o haverá o ´buraco´ do 2 para o 4.
sinceramente eu nunca soube o porquê de controlar isso. geralmente o campo-chave é invisível para o usuário. é utilizado internamente, principalmente para relacionamento entre tabelas.

em tempo: para sua maior comodidade eu sugiro ainda que você trabalhe com tabelas aninhadas. as famosas nested tables.