Páginas

quarta-feira, 7 de março de 2012

TransactionScope e Invalid operation. The connection is closed no Oracle

Tudo na vida acontece por uma razão, e quando a gente não entende a razão considera que é magia.

Digo isso por um problema que estava tendo em uma aplicação ASP.NET que usa banco de dados Oracle. Em determinada funcionalidade, um processamento com gravação, ocorria um problema que impedia a gravação dos registros. O erro era:

System.InvalidOperationException: Invalid operation. The connection is closed.
   at System.Data.OracleClient.OracleConnection.GetOpenInternalConnection()
   at System.Data.OracleClient.OracleConnection.get_ErrorHandle()
   at System.Data.OracleClient.OracleDataReader.ReadInternal()
   at System.Data.OracleClient.OracleDataReader.Read()
   at Microsoft.Practices.EnterpriseLibrary.Data.Oracle.OracleDataReaderWrapper.Read()

Simplesmente eu não entendia a causa desse erro ( neste momento chamado de "magia"), pois o código que rodava essa consulta (sim, consulta, pois você pode ver que na stacktrace estamos usando o DataReader do OracleClient) funcionava de forma isolada sem problemas. Essa conclusão eu cheguei após gerar um windows service e colocar para ficar rodando este método a cada 30 segundos, já que minha primeira desconfiança era algum problema esporádico de comunicação com o servidor Oracle. Bom, esse windows service ficou rodando por horas, sem apontar nenhum problema.

No final do dia, uma coisa me chamou a atenção: processamentos pequenos funcionavam sem problema, os que gravavam muitos dados é que geravam o erro. E mais, todo o código que era executado estava envolvido por um TransactionScope... Na altura do campeonato, resolvi testar tirando a transação, ou seja, passando no seu construtor a opção Suppress (http://msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx):

using(var trans = new TransactionScope(TransactionScopeOption.Suppress)) 
{
    // ...
} 

Dito e feito. Após essa alteração, o código funcionou.

Mas não deixei a rotina assim. Para não ficar com ela de forma que pudesse dar alguma inconsistência na gravação do banco, eu mantive a transação ativa só que coloquei um tempo de timeout maior (http://msdn.microsoft.com/en-us/library/9wykw3s2.aspx). Também funcionou e resolveu o problema.

Em resumo, o problema do Invalid Operation dava quando o tempo de transação padrão, que é 60 segundos, expirava no meio do processamento. Isso impactava também a parte das leituras que eram feitas na base de dados. O problema que eu classifiquei como "magia" poderia ser traduzido como uma descrição de erro não clara. Se logo no começo eu tivesse tomado um erro cuja descrição falasse algo de transação, ficaria mais claro para ir direto na raiz do problema. Mas fica a dica de que tudo tem explicação, só depende de quão fundo você pode e quer ir.

Em tempo, essa rotina que estava com problemas poderia ser melhor escrita. Ou seja, antes de iniciar a transação de gravação, ler e processar tudo o que fosse necessário, deixando apenas para gravar os dados no banco em um último momento e com transação somente nesta parte. Assim que possível vamos fazer isso.

[]'s

Nenhum comentário:

Postar um comentário