Chave primária com ClientDataSet(CDS)

22/11/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


Lero

Respostas

22/11/2005

Emerson

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


Responder Citar

22/11/2005

Lero

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.


Responder Citar

22/11/2005

Lero

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

  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


Responder Citar

22/11/2005

Emerson

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.


Responder Citar

22/11/2005

Lero

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


Responder Citar

22/11/2005

Emerson

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;


Responder Citar

23/11/2005

Lero

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 [b:8404ba2482]onUpdateRecord[/b:8404ba2482], no [b:8404ba2482]DataSetProvider[/b:8404ba2482] só tem [b:8404ba2482]BeforeUptdateRecord e AfterUpdateRecord[/b:8404ba2482]. 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 [b:8404ba2482]OnUpdateError[/b:8404ba2482], só q qdo eu tento alterar o [b:8404ba2482]TField.NewValue[/b:8404ba2482] ele dá erro de [b:8404ba2482]´Falha Catastrófica´[/b:8404ba2482] :shock: . Tô achando q esse problema é por causa da mistura explosiva ADO + DSP + CDS. Alguém já teve esse problema?


Responder Citar

23/11/2005

Emerson

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?


Responder Citar

23/11/2005

Lero

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.


Responder Citar

23/11/2005

Emerson

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.

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...&93;´;
          open;
          if fields&91;0&93;.isnull
          then proximocodigo := 1
          else proximocodigo := fields&91;0&93;.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.


Responder Citar

24/11/2005

Lero

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


Responder Citar

24/11/2005

Emerson

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.


Responder Citar