sexta-feira, abril 12, 2013

FireDAC - Cadastro Mestre/Detalhe


Veja nesse vídeo como criar um cadastro mestre/detalhe, que faz uso de um campo auto-incremento, utilizando o FireDAC.


42 comentários:

Cristiano Chiele disse...
Este comentário foi removido pelo autor.
Cristiano Chiele disse...
Este comentário foi removido pelo autor.
isaquepinheirooficialbr disse...

Gostaria de vê esse mesmo exemplo do video usando o evento OnUpdateRecord do TADQuery, pois já tentei de todas as formas usa-lo e não grava no banco de jeito algum.

Se não usar o evento OnUpdateRecord, funciona 100%

Poderia me enviar um exemplo usando esse evento ?

Alan Glei disse...

Estou preparando um vídeo com os eventos onUpdateRecord e onUpdateError do componente TADQuery

Hugo Fabrício disse...

Estou com problemas no mestre detalhe usando FireBird,
quando uso o Adqry sem Cache funciona normal.

Mas quando habilito o Cache e coloco como no exemplo ele não salva na base e nem da erros
Estou com um exemplo se for o caso posso te enviar !

Alan Glei disse...

Boas notícias! Recebi um retorno da Embarcadero quanto ao problema do CacheUpdate nos bancos IB e FB. O update 1 do XE5 já vem com a correção do problema!

Nilton Oliveira disse...

Alan, quanto o auto incremento para master detail usando firedac, poderia nos passar um vídeo aula sobre o assunto.

Unknown disse...

Olá Alan,

Testei o seu exemplo utilizando a versão xe5 update 2 e retorna o seguinte erro:

[FireDAC] [DatS]-16. Cannot process - no parent row. Constraint [ForeignKeyConstraint].

Saberia me dizer porque deste erro?

LuisP disse...


Mesmo erro do amigo "Olimpio Gonzatto Jr" Sempre usei o Zeos nunca tive problema, para compatibilizar minhas aplicações resolvi usar o bendito firedac que diziam ser top rapido e nativo. Segui o exemplo com sql server e da mesmo problema, não consigo resolver alguem tem a solução ?

[FireDAC] [DatS]-16. Cannot process - no parent row. Constraint [ForeignKeyConstraint].

Unknown disse...

Boa tarde Alan...

Estou enfrentando este problema:

[FireDAC] [DatS]-16. Cannot process - no parent row. Constraint [ForeignKeyConstraint].

Segui religiosamente seus passo a passo no vídeo, exceto pelo detalhe de estar utilizando o banco firebird e o XE6...

Alguma ideia de qual seria o problema?

Alan Glei disse...

Fiz testes aqui com o Interbase e o Delphi XE6, tudo funcionou certinho... se possível me mandem esses códigos que estão gerando erro para que eu possa reproduzir aqui. Ok!

Alan Glei disse...

Consegui simular o erro "[FireDAC] [DatS]-16. Cannot process - no parent row. Constraint [ForeignKeyConstraint].". O que ocorre é o seguinte: vocês estão incluindo um registro filho sem antes salvar o registro mestre... segue um link para um vídeo explicando o problema com mais detalhes Ok!

https://dl.dropboxusercontent.com/u/90063499/Erro%20MestreDetalhe%20FireDAC.mp4

Sakamoto disse...

Bom dia Alan,

Estou trabalhando com o Firedac e Oracle. E fiquei em uma dúvida em relação ao TFDPhysOracleDriverLink e TFDGUIxWaitCursor.

Esses 2 componentes devem estar presentes em todos os TForm que acessam o Oracle? Se eu utilizar um TDataModule só preciso adicionar apenas um para o projeto todo?

Att,

Mytracelog - Registro de um DBA
http://mytracelog.blogspot.com

Mauro disse...

Funciona perfeitamente, mas como ficaria esse mesmo exemplo em um ambiente 3 camadas?

Anônimo disse...

Olá Alan
Estou usando o XE7 e mesmo assim estou com o dilema de não conseguir persistir no banco firebird. Habilitei a opção autoincremento na propriedade dos campos pôs o firebird não tem esse tipo de variável. Quando vou gravar não acontece erro nenhum, porém quando faço o refresh os dados somem.
Segue o trecho que uso para gravar

if not DTMTabelas.FDConnection.InTransaction then
DTMTabelas.FDConnection.StartTransaction;
try
DTMTabelas.FDConnection.ApplyUpdates([DTMTabelas.QryVenda,DTMTabelas.QryPAF,DTMTabelas.QryItemVenda,DTMTabelas.QryMovimento]);
DTMTabelas.FDConnection.Commit;
except
DTMTabelas.FDConnection.Rollback;
end

Felipe disse...
Este comentário foi removido pelo autor.
Felipe disse...

Parabéns pelo video, me ajudou muito.

Gostaria se possível de tirar uma dúvida, estou usando MSSQL e quando dou um post no mestre para inserir os detalhes tenho a exceção [FireDAC][DatS]-15. Duplicate row found on unique index. Constraint [_FD_UC_UniqueConstraint], só dá certo se o dataset mestre estiver vazio e eu inserir um registro, se ele está mostrando alguns registros já inseridos e vou inserir mais um, acontece o erro acima, será que é algum erro de índice ou provider flags?

LMBelo disse...

Rafael Campos, se você estiver utilizando o evento "OnUpdateRecord", você deve altera o valor do parâmetro "AAction" dessa forma: AAction := TFDErrorAction.eaDefault;

Por padrão ele vai como TFDErrorAction.eaApplied, entendendo que você vai fazer o post dos dados no banco, pra isso você pode usar o componente TFDUpdateSQL. Para utilizar esse componente eu te recomendo ler a documentação porquê ele tem alguns detalhes e facilitações.

LMBelo disse...
Este comentário foi removido pelo autor.
LMBelo disse...

Felipe Garrido, isso está acontecendo porque você adicionou um campo na propriedade "IndexFieldNames" e ele não aceita duplicações. O que você pode fazer é, no evento "OnNewRecord" você adicionar um valor incremental nesse campo index, ex: "MEU_CAMPO_INDEX" := DataSet.RecordCount + 1. Para gerar o valor real desse campo, o que será persistido no banco de dados, você deve utilizar o evento "OnUpdateRecord" e gerar o valor para esse campo em uma transação, assim:

if (ARequest in [TFDActionRequest.arInsert]) then
begin
DataSet.Edit();
DataSet.FieldByName('MEU_CAMPO_INDEXADO').AsInteger := GeraCampoInc('MEU_CAMPO_INDEXADO');
DataSet.Post();
end;
AAction := TFDErrorAction.eaDefault

LMBelo disse...

Sakamoto, basta declarar um único local, geralmente deixo junto à declaração da conexão com o banco.

LMBelo disse...

Mauro, utilizando um webservice Restfull, quando o objeto for serializado, ao invés do atributo "type", o framework vai passar o atributo "ref" que é a referência ao ponteiro do objeto, e assim, entre as requisições, você pode ir trabalhando com o dataset. Se você estiver utilizando Restless, o melhor é você não trabalhar com datasets em memória entre as pontas(cliente/servidor).

No servidor, ex: a tabela de estados pode estar em um dataset que você vai abrí-lo somente na primeira requisição e mantê-lo em uma variável global ou de classe(pode-se utilizar o padrão singleton), lembrando que deve-se tomar cuidado com o acesso concorrente(thread-safe).

Essa prática é melhor utilizada no lado cliente, onde você pode fazer várias alterações nos dados e depois enviar um batch contendo todas as alterações, podendo inclusive ser o "delta".

Unknown disse...

Olá amigos! Primeiramente gostaria de agradecer a vc Alan pelos excelente post. Tenho uma dúvida. O exemplo funcionou certinho relacionando a tabela master com a detail. Mas tenho a necessidade de mais um relacionamento com a tabela detail e não com a master. Exemplo

MASTER-->DETAIL-->3º TABELA

Isso é possível utilizando cache?

Obrigado
Sorrilha

Alan Glei disse...

Mesmo procedimento, faça o relacionamento do seu novo dataset com o Detail e não esqueça de apontar o SchemeAdapter neste novo Dataset Ok!

Unknown disse...

Obrigado pela resposta Alan. Mas o problema tá aí! Se ligar com o detail a chave primaria do master e detail (em modo de insert) estrarão -1 assim não deu certo. Na terceira tabela tenho:

id_master (-1)
id_detail (-1)
id_terceira_tabela (identy)

o relacionamento grava com -1
Obrigado mais uma vez.

Tiago Melo disse...

Alan, show de bola os posts, obrigado por compartilhar com a comunidade seu conhecimento.
Enfim estou passando pelo problema que já foi citado aqui: "Cannot process - no parent row. Constraint [ForeignKeyConstraint]", estou usando XE7 + SQL Server, eu reparei que no video que vc postou sobre esse assunto e realmente ocorre como vc mostrou ou seja caso não tenha dado post no mestre ocorre o erro acima, porém vc realiza o post no grid do mestre com seta pra cima e para baixo, no meu caso aqui o usuario do meu sistema não vai realizar esse procedimento de dar seta pra cima e para baixo...tentei fazer o post no mestre via codigo...QrMestre.Post e mesmo assim o erro persistiu, saberia me dizer como contornar essa situação?

Tiago Melo disse...

bom depois de bater cabeça,encontrei um post onde se fazia isso no botão "novo"
QrMestre.Append;
QrMestre.Post;
QrMestre.Edit;

na minha opnião não deveria ser isso, pois como se trata de nova inclusão não justifica o edit abaixo, pois dessa forma o estado do dataset passa a não mais ser de insert e sim de edit, o que dependendo da situação pode prejudicar a escrita do codigo, na minha opnião é BUG. Mas enfim funcionou e vou usar o MD no firedac pois vou poupar muitas horas de desenvolvimento com esse modelo.

Unknown disse...

Boa tarde Alan, parabéns pelo post, esclareceu muito para mim, seria possível você demonstrar como realizar persistência mestre detalhe usando DataSnap/REST? tenho feito vários testes aqui e não estou conseguindo persistir os dados, desde já obrigado.

Unknown disse...

Bom dia Allan !! Primeiramente obrigado pelo conteúdo de valor e sua disposição em ensinar.
Segui todo o procedimento certinho e deu tudo certo, mas notei que funciona bem para inserts e edits, ou seja, novos registros e alterações de registros já existentes. Mas e a deleção? A deleção pode ser feita em cache também? Tentei aqui e não consegui... ou precisamos fazer a deleção diretamente nos datasets e trata-la separadamente? E se tiver que ser feita separadamente é necessário então fazer um Dataset.Delete em cada dataset ou apenas no mestre e o resto ocorre em cascata?

Unknown disse...

Bom dia Alan..... parabens pelo vídeo e tambem obrigado pela ajuda quer voce nos esta proporcionando. Uma duvida.... vcs saberia me dizer porque nao me aparece erro nenhum quando da inclusao no detalhe de chave duplicada?.... nao me da o erro de Key Violation?

jasoft disse...

Boa Tarde Alan.
Parabéns pelo post. Vi que você conseguiyu simular o erro "[FireDAC] [DatS]-16. Cannot process - no parent row. Constraint [ForeignKeyConstraint].". O que ocorre é o seguinte: vocês estão incluindo um registro filho sem antes salvar o registro mestre... segue um link para um vídeo explicando o problema com mais detalhes Ok!
Vi também o seu video em https://dl.dropboxusercontent.com/u/90063499/Erro%20MestreDetalhe%20FireDAC.mp4

Sei que o seu post é antigo, mas estou usando o XE8+Firedac+Firebird e também obtive o mesmo erro que foi obtido pelo outros colegas acima. Pergunto. Trata-se de um erro no FIREDAC e se o meesmo já foi corrigido pela Embarcadero? Se não, não entendi porque tenho que salvar o registro mestre antes (pois entendi que com CacheUpdate isso é feito em sequencia pelo comando AppplyUpdate do TADSchemaAdapter?

Unknown disse...

Olá pessoal,

No projeto de exemplo "SampleDataSnapFireDAC_Client.dpr" em "Object Pascal \ DataSnap \ FireDAC" que acompanha o Delphi 10 Seattle e outras versões anteriores, adicionei dois botões na unit "ClientUnit.pas". O primeiro botão faz uma inserção e o segundo exclusão de registros na query mtOrders, no entanto, no "Post Updates", é levantada uma uma exceção no lado do servidor: FireDAC [DatS] -16. Can not process - no parent. Restriction [ForeingnKeyConstraint]. Alguém já passou por esse problema para me ajudar a encontrar uma solução?

Código nos botões:
-------------------
procedure TClientForm.AddClick (Sender: TObject);
begin
DataModuleFDClient.mtOrders.Append;
end;

procedure TClientForm.DeleteClick (Sender: TObject);
begin
DataModuleFDClient.mtOrders.Delete;
end;
----

Muito obrigado,

Adeildo Silva

samuka disse...

Boa tarde Alan, fiz seu exemplo master detail, e algo curioso quando faço a inserção pela grid, salva os dados normais mais quando insiro pelos edits só salva a tabela master e os dados da grid detail somem
1 botão dou um append na master
2º botão insiro os itens na grid details
3º botão para o applyupdates(0)

Angelo Sobreira disse...

Existe algum exemplo de como fazer isso que é utilizado multi-camadas, que obrigatoriamente temos que ter um TDataSetProvider e um TClientDataSet

Unknown disse...
Este comentário foi removido pelo autor.
Unknown disse...

Olá Alan!

Estou passando o mesmo problema que alguns dos nossos colegas aí.
Utilizado o Delphi Seatle e Firebird 3, crio o autoincrement nativo desta versão do Firebird, segui todos os passos discriminados acima mas o detalhe dos itens sempre somem ao executar o applyupdate no SchemaAdapter, não sei mais o que fazer, já tentei de tudo.

Observei que ao ativar a Query, a propriedade fiDetails do cache no FetchOptions é desmarcado automaticamente, o que posso estar fazendo de errado ?

Desde já agradeço sua preocupação em contribuir com a comunidade de desenvolvimento

Leão Novo disse...

Já re-visei várias vezes, assistindo o video sobre isso e continua mensagem
[FireDAC][DatS]-2.Object[tabela] is not found.

Na tabela filha, ao fazer o relacionamento MasterSouce, ocorre esse erro,
quando tiro não acontece mas não grava o campo chave primaria.
O que pode ser isso?

Uso seatlle, firebird 2.1

Agradeço,
Leão

Unknown disse...

Olá, Alan! No meu projeto ao executar DataSet_Detalhe.Post dá erro de chave estrangeira nula. No caso, a chave estrangeira que se refere à tabela pai. A navegação está correta, o que confirma que está configurado como você mostrou. Sabe o que posso estar fazendo de errado?

Unknown disse...

Modifiquei no banco para aceitar valores nulos, pois estava marcado para não permitir valor nulo. Agora está dando erro de valor negativo não permitido para a chave estrangeira. Como posso proceder?

Unknown disse...

Resolvido, Alan! Era um problema interno.

Luiz Carlos Fagundes disse...

Olá Alan, muito bom seu vídeo fácil de compreender! Gostaria de pedir sua ajuda sobre mestre detalhe firedac em um ambiente multicamadas, cara to quebrando a cabeça mas ainda não consegui fazer pra rodar na maquina cliente!Poderia por favor nos dar uma luz de como fazer isso!

Fabrício Melo - Gyn disse...

Sobre o erro "Cannot process - no parent row. Constraint"

Coloquei nos Eventos Before Insert / Edit dos Detalhes para verificar se o seu Mestre esta em Alguma Alteração...

procedure TDMXXXX.DataBeforeInsert(DataSet: TDataSet);
begin

//APLICAR O POST NO REGISTRO MESTRE ANTES
if Assigned(TFDQuery(DataSet).DataSource) then
if Assigned(TFDQuery(DataSet).DataSource.DataSet) then
if TFDQuery(DataSet).DataSource.DataSet.State in [dsInsert] then
begin
TFDQuery(DataSet).DataSource.DataSet.Post;
TFDQuery(DataSet).DataSource.DataSet.Edit;
end;

inherited;
end;

Resolveu, porém pra trabalhar um formulário com os "DataSource.AutoEdit = True" ligados nas Querys, Mestre e Detalhes, tive problemas, pois se o Usuário começar a digitar em um DBEdit a validação acima faz com que os primeiros caracteres digitados sejam perdidos...