Usuários mais ativos no mês 12/2019
  1. henrique.muller

    843 Pontos

  2. paulo.silva

    842 Pontos

  3. maicon.pereira

    582 Pontos

  4. juliano.pezzini

    464 Pontos

  5. leandro.piscke

    428 Pontos

  6. diuari.molinari

    386 Pontos

  7. dirceu.schlickmann

    347 Pontos

  8. Camila

    288 Pontos

  9. bruno.cardoso

    272 Pontos

  10. Rafael Leonhardt

    253 Pontos

"Operação Inválida em um objeto fechado" ao tentar acessar o valor de um campo do TBSQLNativeControl em Oracle

+5 votos
35 visitas

Em uma base Oracle, após dar um LoadAllFromQuery(TBSQLNativeControl), eu não consigo mais acessar os dados, como se estivessem bloqueados, o que não ocorre em base SQLServer.

A alteração que fiz funciona normalmente em SQL Server, mas em Oracle, quando chega na atribuição do campo "OCCLIENTESEQITEM", logo após o LoadAllFromQuery, dá o erro "Operação Inválida em um objeto fechado":

sql := TQueryObject.Create('CM_ITENS', 'A');

sql.AddFields('A', ['OCCLIENTESEQITEM SEQITEMINTEIRO']);

_sql.SetQueryObject(sql);

_itens.GetFieldsFromQuery(_sql.NativeDataSet);

_itens.AddFields('OCCLIENTESEQITEM:STRING;');

_itens.LoadAllFromQuery(_sql.NativeDataSet);
_itens.FindField('OCCLIENTESEQITEM').AsString := _sql.NativeDataSet.FieldByName('SEQITEMINTEIRO').AsString;

Fazemos este "malabarismo" com o campo "OCCLIENTESEQITEM", porque precisamos dele como string para conseguir representar valores não siginificativos no container, no banco tem que ser inteiro mesmo.

Além do erro mencionado, no Oracle não conseguimos acessar os campos do dataset durante depuração, após a execução do LoadAllFromQuery. Ex.: _sql.NativeDataSet.FieldByName('SEQITEMINTEIRO').AsString dá erro no evaluator (Ctrl + F7) do Delphi.

Vendo as propriedades desse native control, logo após a execução do LoadAllFromQuery, pude ver algumas diferenças entre os bancos (imagem em anexo), a principal foi que a propriedade FNestedDataSetClass muda de um tipo de base para outra, será que é isso que faz os dados ficarem inacessíveis? 

Estamos fazendo algo errado?

Stack do online debugger: http://desenv.benner.com.br/accessviolation/?qa=blob&qa_blobid=6117336191396093861, na linha 4208 ocorre o erro de Operação inválida em um objeto fechado.

perguntou Ago 29 por vinicius.andrade (21 pontos)
editado Ago 30 por vinicius.andrade
Verifique se existe algum erro pelo online debugger. E adicione na pergunta uma stack trace do erro ocorrido.
Stack do online debugger: http://desenv.benner.com.br/accessviolation/?qa=blob&qa_blobid=6117336191396093861, na linha 4208 ocorre o erro de Operação inválida em um objeto fechado.

1 Resposta

+1 voto
 
Melhor resposta
Em fato, o componente TBSqlNativeControl vai apresentar este exato comportamento mesmo. A solução é não ler os campos depois que foi alcançado o final do cursor (Eof = True).

A ideia é abrir a query, ler os registros um por um até acabar e "fim". Cada vez que acionar o "Next" as informações do registro anterior foram descartadas e não podem mais ser acessadas. Também não é possível acessar as informações dos próximos registros (a não ser que se faça Next até chegar a eles). Também por isto que não é confiável usar a propriedade "RecordCount" num TBSqlNativeControl.

Este componente existe inspirado pelo comportamento dos cursores do Oracle, e os cursores do Oracle também se comportam desta maneira (quando chegou no final não é mais possível ler as informações), isto explica também pq o comportamento só é apresentado quando em Oracle, no SQLServer os comportamentos dos Cursores são simulados (para compatilidade) mas não se usufrui dos benefícios (velociadade/memória) do uso de Cursores, desta forma também não acontecem os impactos.

Lembrando que se usar o TBQuery nada disto se aplica, é uma escolha que o dev precisa fazer: se vai precisar dos benefícios oferecidos pelo TBSqlNativeControl, então vai ter que "pagar o preço".

Se não pode/quer pagar o preço, então usa o TBQuery.
respondida Set 5 por juliano.pezzini (464 pontos)
selecionada Set 6 por henrique.muller
É melhor manter o TBSqlNativeControl pela velocidade. Então você poderia fazer a atribuição do campo com o DContainer, e não com a query.

...
_itens.LoadAllFromQuery(_sql.NativeDataSet);
_itens.FindField('OCCLIENTESEQITEM').AsString := _itens.FindField('SEQITEMINTEIRO').AsString;
...