Considere as seguintes tabelas numa relação Master-Detail:Os campos id_master (PK), id_detail (PK) e id_detail2 (PK) são inteiros e os desc_master, desc_detail e desc_detail2 são Varchar. O campo id_master é FK na tabela detalhe de primeiro nível, table_detail. O campo id_detail é FK na tabela detalhe de segundo nível, table_detail2. As PKs são todas auto-incremento com valor gerado no servidor (de aplicação).
Tenho um DataModule com 3 DataSets (no meu caso, usei TADOQuery) contendo os seguintes comandos SQL:
- qryMaster:
SELECT * FROM table_master
- qryDetail:
SELECT * FROM table_detail WHERE id_master = :id_master
- qryDetail2:
SELECT * FROM table_detail2 WHERE id_detail = :id_detail
Existem ainda 2 componentes TDataSource ligando o qryDetail ao qryMaster (dsMaster) pela propriedade DataSource, e também um ligando o qryDetail2 ao qryDetail (dsDetail). Desta forma, tenho um DataSetProvider ligado ao DataSource dsMaster que irá prover os dados aos meus ClientDataSet. Eis meu DataModule:
O cdsMaster está ligado ao DataSetProvider, prvMaster, o cdsDetail ligado ao DataSetField cdsMasterqryDetail, e o cdsDetail2 está ligado ao DataSetField cdsDetailqryDetail2. Uma construção comum quando se trata de relação master-detail usando DataSnap.
O fato de estar tudo junto (apenas 2 camadas) facilita o entendimento e o debug. Mas poderia estar distribuído, com as queries e o provider num servidor de aplicação e os ClientDataSets na aplicação cliente.
ProviderFlags dos campos configurados corretamente (incluído pfInKey para id_master, id_detail e id_detail2 nas 3 queries), um form simples com 3 DBNavigators e 3 DBGrids ligados aos 3 ClientDataSets. Pois bem, era de se esperar que tudo funcionasse as mil maravilhas na primeira tentativa, certo? Errado!
Ao rodar o programa, inseri dados na tabela master e salvei, chamando então cdsMaster.ApplyUpdates. Tudo Ok. Inseri também registros no cdsDetail, tudo Ok. Ao rodar a aplicação e inserir registros no cdsDetail2 (o detalhe de segundo nível) e chamar o cdsMaster.ApplyUpdates, kabooommmm! Lá vem o erro "Link Fields to detail must be unique". Não fazia o menor sentido uma vez que os FK's são corretamente preenchidos automaticamente pelo DataSnap e os ID's são preenchidos com valores únicos negativos. Não havia nada errado!
O erro acontece ao aplicar os updates. Mas nem chega ao Provider. O erro acontece mesmo no ClientDataSet ANTES de enviar o Update ao Provider.
Aí fui debugar usando o DBClient.pas. O erro ocorre no método TCustomClientDataSet.InternalPost, na linha:
Check(FDSCursor.InsertRecord(ActiveBuffer));
Ou seja... Como isto ocorre dentro do Midas.dll (ou MidasLib.dcu), não há como debugar além deste ponto.
Usei o Google atrás de uma solução, fiz todas as alterações possíveis que pude imaginar para ver se o erro cessava, mas nada funcionava.
Depois de um tempo pensando, eu me lembrei que esta construção onde a PK da tabela master vira FK da tabela detalhe não me é usual. Eu sempre propago as chaves do mestre para compor a chave do detalhe, da forma:
table_detail:
- id_master (PK)
- id_detail (PK)
- desc_detail
table_detail2:
- id_master (PK)
- id_detail (PK)
- id_detail2 (PK)
- desc_detail
Após isto o Update ocorre normalmente, sem erros. Ainda esta semana vou anexar o código fonte completo do projeto de exemplo.
3 comments:
Ótimo! precisei disso hoje!
Olá mas esse erro não acontece também se por exemplo: Seu bd está com um registro gravado com id unique de número 2 vamos supor que você está com CDS aberto e este registro de número 2 está lá na sua tela carregado no CDS, ai você começa a inserir registros temporários antes de dar um Update pro Server, inseriu o primeiro beleza ele coloca o código 1 "temporário até ir pro Server e retornar o valor do bd" ai você insere o segundo e tenta sair do registro pimba vem o "LinkFields to ..." por causa dos dois registros com número 2.
Post a Comment