Ir para conteúdo
  • Cadastre-se

dev botao

Dúvida avançada IBX (TIBQuery) e Unidac (TUniQuery)


  • Este tópico foi criado há 1992 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Recommended Posts

Boa tarde a todos!

Pessoal, preciso de um auxílio,

Vou explicar a situação:

Possuo uma Query (Exemplificando, pode ser do tipo TUniQuery)
que possui os TFields adicionados nela. Ela foi "arrastada" para um DataModule, e os TFields adicionados em tempo de design.

Um exemplo dessa situação:

select id_empresa, nome_empresa from empresas

Na UniQuery1 (TUniQuery) tenho dois Fields, ID_EMPRESA e NOME_EMPRESA adicionados, ou seja, UniQuery1ID_EMPRESA Integer e UniQuery1NOME_EMPRESA String

Ok, até ai tudo tranquilo:

Pra preencher ela, ao invés de usar o Open tradicional por um SQL informado, usamos classes e ORM, objetos e Listas e RTTI de um produto comercial, que transforma os dados colhidos do BD para o formato herdado TDataset

Mais ou menos assim:

UniQuery1 := TransformeemDataset('select id_empresa, nome_empresa from empresas'); (Simplificando a forma que utilizamos esse produto comercial fechado que não temos acesso ao código fonte para alterar)
Esse método traz corretamente os dados para a UniQuery1.

Ao acessar UniQuery1.Fields[0].AsInteger em tenho o código, UniQuery1.Fields[2].AsString eu tenho o nome da empresa tbm.
Ele também trás propriedades corretas, Como RecNo, RecordCount, etc.. tudo certinho... se eu tiver 4 registros nessa tabela do BD ele traz RecordCount = 4

Também consigo recuperar os dados, usando FieldByName. exemplo: UniQuery1.FieldByName('ID_EMPRESA').AsInteger também retorna corretamente.

Porém, ao acessar diretamente o TField da seguinte forma: UniQuery1ID_EMPRESA.AsInteger ou UniQuery1NOME_EMPRESA.AsString, não recebo valor algum.

Ai vem a questão... a utilização desse produto comercial fechado é para um projeto legado grande, e a intenção é ter o mínimo possível de trabalho na migração para este produto comercial.. porém, no código fonte do programa legado, 90% das referencias estão da forma direta, UniQuery1ID_EMPRESA.AsInteger... milhares de referencias no código ....

E é justo essa forma de chamada que não está funcional

Gostaria de pedir ajuda dos mestres, se existe alguma forma de utilizar algum mapeamento de nomes dos TFields, Ou RTTi, ou alguma coisa que torne possível eu trazer para os nomes dos campos nas Querys os valores que já estão nela, de uma forma viável .. Basicamente, os valores estão na Query porém, precisamos acessar de forma direta o TField e isso não estamos conseguindo..

Existe alguma forma de mapear os nomes aos valores, usar ponteiros, Rtti ou coisa do tipo?

Alguém pode nos ajudar?

Att


Rodrigo

Link para o comentário
Compartilhar em outros sites

  • Consultores
24 minutos atrás, teocomp disse:

UniQuery1 := TransformeemDataset('select id_empresa, nome_empresa from empresas');

Quando você faz isso, você está literalmente sobrepondo o objeto UniQuery1 inicial por outra. Você mudou a referência de UniQuery1 para outro objeto. O objeto que existia antes quando você "arrastou para o datamodule" e os campos que adicionou em tempo de desing agora se perderam.

Por isso você não consegue acessar os campos tipo UniQuery1ID_EMPRESA:

27 minutos atrás, teocomp disse:

ao acessar diretamente o TField da seguinte forma: UniQuery1ID_EMPRESA.AsInteger ou UniQuery1NOME_EMPRESA.AsString, não recebo valor algum

 

35 minutos atrás, teocomp disse:

Existe alguma forma de mapear os nomes aos valores, usar ponteiros, Rtti ou coisa do tipo?

Existe várias maneiras. Depende de como você quer fazer. Por exemplo, o código abaixo funciona:

UniQuery1 := TransformeemDataset('select id_empresa, nome_empresa from empresas');
UniQuery1ID_EMPRESA := UniQuery1.Fields[0];

 

  • Curtir 1

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Link para o comentário
Compartilhar em outros sites

Citar

Existe várias maneiras. Depende de como você quer fazer. Por exemplo, o código abaixo funciona: 

Obrigado Elton, realmente funciona dessa forma!

Porém, uma instrução dessa para cada campo adicionado seria um trabalho enorme...

Você disse que existem outras formas... Quais?

Existe alguma forma de eu criar um método/função para automatizar esse processo? Como seria? Usando RTTi?

Desde já agradeço!

Rodrigo

 

 

 

Link para o comentário
Compartilhar em outros sites

  • Consultores
20 minutos atrás, teocomp disse:

Porém, uma instrução dessa para cada campo adicionado seria um trabalho enorme...

Você disse que existem outras formas... Quais?

Existe alguma forma de eu criar um método/função para automatizar esse processo? Como seria? Usando RTTi? 

Algum trabalho você vai ter, mesmo que automatize. Não vou ter condições de elaborar nada porque estou saindo em viagem daqui a poucas horas para o Dia do ACBr.

O mais importante é você entender os princípios:

  • As variáveis como UniQuery1ID_EMPRESA  são Objetos (TFields) passados por referência que acessam um outro objeto TDataset.
  • UniQuery1 é um objeto TDataset.
  • Quando você fez "UniQuery1 := xxx" você sobrepôs a referência original e os objetos de campos ficaram perdidos

 

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Link para o comentário
Compartilhar em outros sites

Perfeito eu estava estudando alguma forma

Descobri o método GetFieldNames dos TDataSets que retorna uma StringList com o nome dos campos

Mas to travado até ai.. Vou continuar os estudos pra tentar elaborar um método que automatize o

UniQuery1ID_EMPRESA := UniQuery1.Fields[0];

com um laço For por exemplo, algo do tipo.. recebendo o nome da Query , pegando os campos e tentando fazer essa atribuição.. só não sei como ainda, mas vou estudar

Abraço Elton e obrigado pela grande ajuda! Boa viagem!

Rodrigo

 

 

Editado por teocomp
Link para o comentário
Compartilhar em outros sites

Boa tarde pessoal,

Alguém pode ajudar?

Isso aqui não está compilando, diz que o lado esquerdo não pode ser atribuido:

procedure TData.Mapear_Fields(Nome_Query: String; Tipo_Query: Integer);
var Aux_Nome: TStrings;
    i: Integer;
begin
  Aux_Nome := TstringList.Create;
  TDataSet(Nome_Query).GetFieldNames(Aux_Nome);
  for i := 0 to Aux_Nome.Count-1 do
  begin
   TField((Nome_Query + Trim(Aux_Nome[i]))) := TDataSet(Nome_Query).FieldByName(Aux_Nome[i]); // <<-- Essa linha não compila
  end;
  Aux_Nome.Free;
end;

Alguma luz?

 

Link para o comentário
Compartilhar em outros sites

  • Consultores

Estou na viagem já, procure pelo método findcomponent do datamodule

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Link para o comentário
Compartilhar em outros sites

Ola pessoal,

Criamos o seguinte:

 

procedure Data.Mapear_Fields(Nome_Query: String; Tipo_Query: Integer);  //Tipo_Query 1 = IBX 2 = Unidac
var Aux_Nome: TStrings;
    i: Integer;
    Nome_Field: String;
begin
  Aux_Nome := TStringList.Create;
  if Tipo_Query = 2 then
    TUniQuery(FindComponent(Nome_Query)).GetFieldNames(Aux_Nome)
   else
    TIBQuery(FindComponent(Nome_Query)).GetFieldNames(Aux_Nome);

  for i := 0 to Aux_Nome.Count-1 do
  begin
    Nome_Field := Nome_Query + Trim(Aux_Nome[i]);
    showmessage(TUniQuery(FindComponent(Nome_Query)).FieldByName(Aux_Nome[i]).AsString);
    if Tipo_Query = 2 then
      TField(Nome_Field) := TUniQuery(FindComponent(Nome_Query)).FieldByName(Aux_Nome[i])
     else
      TField(Nome_Field) := TIBQuery(FindComponent(Nome_Query)).FieldByName(Aux_Nome[i]);
  end;
  Aux_Nome.Free;
end;

 

Compila sem problemas, porém, o resultado nos fields vem vazio.. debugando.. a atribuição a TField(Nome_Field) funciona ok, GetFieldNames tbm funciona ok, a TString é preenchida com a lista de campos corretamente

O problema está na atribuição onde tem o FieldByName (Ou Fields[x], tbm testamos e dá o mesmo problema)

Ao Chamar a Query direto sem o findcomponent ali dá certo, mas sem o findcomponent ( somente com o nome da variavel ) não dá, e recebemos um AV

Resumindo, o problema está em TUniQuery(Findcomponent(Nome_Query))

TUniQuery(Nome_Query) não funciona e o código acima compila mas os campos vem vazios.. Em aux_nome esta tudo correto, e se eu trocar por MinhaQuery.FieldByName(Aux_Nome) tudo ok tbm...

Porém, como queremos uma função genérica, isso não é possível... (Desconsiderem o showmessage do código !!! )

Acho que estamos indo pro caminho errado talvez.. talvez uma nova referencia ao DataSet esta sendo alimentada em branco, etc... Qual seria o caminho correto?

Como proceder?

 

Desde já agradecemos

 

Rodrigo

 

 

 

 

 

Link para o comentário
Compartilhar em outros sites

  • Consultores
4 horas atrás, teocomp disse:

TField(Nome_Field)

Isso não vai funcionar. Você está tentando transformar uma string (Nome_Field) em um TField. É como se você dissesse:

- Tá vendo essa string? Na verdade ela é um TField.

Mesmo que compile vai dar erro na execução, pois string não é TField, nem por herança. O que deveria estar aí é o objeto que você encontrou buscando com o método "findcomponent".

 

  • Curtir 1

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Link para o comentário
Compartilhar em outros sites

Ao alterar pra

 

TField(FindComponent(Nome_Field)

 

não compila, ele diz :

Left side cannot be assigned to

 

O problema esta no lado direito da atribuição?

Isso aqui então:

TUniQuery(FindComponent(Nome_Query)).FieldByName(Aux_Nome[i])

 

Porque, debugando, essa parte acima vem sempre vazio, retorna um TField vazio...

 

Link para o comentário
Compartilhar em outros sites

  • Consultores
4 horas atrás, teocomp disse:

não compila, ele diz :


Left side cannot be assigned to

Se não conseguir fazer compilar, o outro lado não vai adiantar...

 

4 horas atrás, teocomp disse:

Porque, debugando, essa parte acima vem sempre vazio, retorna um TField vazio... 

Pode ser porque a conexão não foi feita ainda.

  • Curtir 2

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Link para o comentário
Compartilhar em outros sites

Ok,

Realmente nesse caso a conexão não foi feita, e nem será feita... Nós usaremos o DataSet apenas como um "containner", ele não está diretamente conectado ao banco nesse caso...

Ainda não conseguimos sucesso, porém acredito que a grande questão está onde vc nos explicou Elton, que a atribuição a Query inicia uma nova instancia...

Aqui

UniQuery1 := TransformeemDataset('select id_empresa, nome_empresa from empresas');

Nesse caso, nossa Uniquery1 não funciona da forma que desejamos , pois o sistema passa ela por referencia em diversos métodos, o sistema usa herança tbm, enfim, o fato de criar essa nova instancia da Query tem sido o grande problema ..

Vc tem algum material avançado de Datasets, ou mesmo de IBXQuery ou UniQuery para entendermos melhor essa questão da nova instancia, se existe alguma forma de usar o mesmo "objeto" sem criar essa nova referencia?

A função pra automatizar a atribuição dos campos ali, tbm não deu certo ainda, mas acreditamos que se o resultado de TransformeEmDataset() ficasse na Uniquery1 que esta em tempo de design (arrastada) no form, ou no datamodule, nossos problemas seriam sanados

Onde consigo mais informações sobre isso?

Att

 

Rodrigo

 

Link para o comentário
Compartilhar em outros sites

  • Consultores
2 horas atrás, teocomp disse:

Onde consigo mais informações sobre isso?

Mas esse problema não é exatamente sobre a IBXQuery ou UniQuery. É sobre objetos e referência.

2 horas atrás, teocomp disse:

Realmente nesse caso a conexão não foi feita, e nem será feita... Nós usaremos o DataSet apenas como um "containner", ele não está diretamente conectado ao banco nesse caso... 

Bom, em algum momento ele precisa se comunicar com o BD. Então a conexão precisa ser feita. A menos que estejamos falando de coisas diferentes aqui... Mas pelo que você mencionou anteriormente, a conexão funcionava. Veja o que você mesmo escreveu antes:

Em 08/11/2018 at 08:43, teocomp disse:

Ao acessar UniQuery1.Fields[0].AsInteger em tenho o código, UniQuery1.Fields[2].AsString eu tenho o nome da empresa tbm.

Isso significa que aconteceu a conexão.

 

2 horas atrás, teocomp disse:

Nesse caso, nossa Uniquery1 não funciona da forma que desejamos , pois o sistema passa ela por referencia em diversos métodos, o sistema usa herança tbm, enfim, o fato de criar essa nova instancia da Query tem sido o grande problema ..

É um pouco difícil orientar vocês porque não entendi muito bem o que vocês querem fazer nem sei qual é o framework que vocês estão utilizando para fazer o ORM.

 

  • Curtir 1

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Link para o comentário
Compartilhar em outros sites

Bom dia Elton!

 

Nós usamos o Mormot Framework,

Na verdade, estamos trabalhando em transformar o sistema legado em um cliente, de um servidor Mormot, que roda MicroServiços SOA, (usando REST e JSON). A conexão é feita pelo Mormot, que busca os dados no banco de dados e devolve em JSON puro.

O Mormot possui um método que converte o JSON puro em formato DataSet (Método JSONtoDataset) e o resultado desse processamento colocamos em componentes IBXQuery e UniQuery. Acho que ai que esta o grande problema, como vc disse, objetos e referencia!

Na Query em si, não existe "conexão", deixamos elas inclusive Active := False e com a propriedade DataBase := nil, Mas os dados são populados corretamente nelas, porém com essa questão da referencia, não conseguimos acessar as propriedades pelos nomes dos campos (TFields) e isso é usado no programa todo milhares de vezes. Na verdade estamos tentando achar uma forma de não passar milhares de vezes no código do programa legado pra tornar viável essa transformação da aplicação.

Será que se a gente processar os dados resultado do Mormot e preencher os datasets manualmente, (Query), sem usar a função JSOntoDataSet do Mormot mas fazendo manualmente mesmo, por laços e preenchando campo a campo.. os DataSets (Query) precisam estar ativos (vai exigir conexão?)

Seria esse o caminho?

Abraço e obrigado pela ajuda

Rodrigo

 

 

Link para o comentário
Compartilhar em outros sites

  • Consultores
1 hora atrás, teocomp disse:

Nós usamos o Mormot Framework,

Na verdade, estamos trabalhando em transformar o sistema legado em um cliente, de um servidor Mormot, que roda MicroServiços SOA, (usando REST e JSON). A conexão é feita pelo Mormot, que busca os dados no banco de dados e devolve em JSON puro.

Legal! O mORMot é uma ferramenta muito poderosa e, na minha opinião, é ideal para o que está querendo fazer. Se começar a usar ele vai querer jogar fora os TDatasets. ?

 

2 horas atrás, teocomp disse:

O Mormot possui um método que converte o JSON puro em formato DataSet (Método JSONtoDataset) e o resultado desse processamento colocamos em componentes IBXQuery e UniQuery. Acho que ai que esta o grande problema, como vc disse, objetos e referencia! 

Na Query em si, não existe "conexão", deixamos elas inclusive Active := False e com a propriedade DataBase := nil, Mas os dados são populados corretamente nelas, porém com essa questão da referencia, não conseguimos acessar as propriedades pelos nomes dos campos (TFields) e isso é usado no programa todo milhares de vezes.

Realmente, você ter que alterar um código chamado milhares de vezes não é o desejado. Se não me engano a unit SynDBVCL.pas pode ajudar nessa conversão.

Mas você precisa mesmo de um pouco de cautela. O que você colocar no componente IBXQuery e UniQuery em tempo de design, não vai funcionar em tempo de execução. Afinal, você mesmo admitiu que não está usando a conexão desses componentes. Quando você faz:

20 horas atrás, teocomp disse:

UniQuery1 := TransformeemDataset('select id_empresa, nome_empresa from empresas');

Você substituiu a query desenhada em tempo de design, e consequentemente os campos (TFields) adicionados em tempo de design também param de funcionar.

Um detalhe é que isso acontece toda vez que o comando acima for executado, cada vez apontando para uma referência diferente.

2 horas atrás, teocomp disse:

Será que se a gente processar os dados resultado do Mormot e preencher os datasets manualmente, (Query), sem usar a função JSOntoDataSet do Mormot mas fazendo manualmente mesmo, por laços e preenchando campo a campo.. os DataSets (Query) precisam estar ativos (vai exigir conexão?)

Seria esse o caminho?

O mORMot já tem uma função que retorna o Dataset preenchido. Se não me engano um TClientDataset. Veja esse post no blog deles, embora um pouco desatualizado, indica a unit que mencionei acima.

A conexão sempre vai ser necessária, de uma maneira ou de outra, mesmo que por fora do TDataset. No seu caso, a conexão terá que ser feita pelo mORMot.

Processar os dados manualmente e preencher um TDataset daria mais trabalho do que substituir o seu design atual. Eu não recomendaria isso de forma alguma. É preferível remover de vez os componentes em tempo de design e passar a usar só o mORMot. Com certeza daria um trabalho braçal agora, mas tornaria seu aplicativo muito mais clean, fácil de dar manutenção e com uma possibilidade de erros muito menor.

A outra opção mais interessante que vejo é tentar fazer funcionar o proposto no começo: remapear todos os campos em tempo de design para os campos criados pelo mORMot.

Note no entanto que você teria que fazer isso para cada query existente no seu datamodule e toda vez que a query for ativada. Ou seja, você teria criar um método que faz o mapeamento dos campos e substituir em todos os lugares que é executado comandos semelhantes aos abaixo:

UniQuery1.Open;
//ou
UniQuery1.Active := True;

 

 

  • Curtir 1

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Link para o comentário
Compartilhar em outros sites

  • Este tópico foi criado há 1992 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.