BT

Os problemas do WCF e os blocos Using

por Jonathan Allen , traduzido por Carlos Mendonça em 20 Mar 2009 |

Clientes de um serviço WCF não podem ser utilizandos dentro de um bloco Using porque eles podem inesperadamente lançar uma exceção. E mesmo que você capture esta exceção, é possível que uma conexão fique aberta. Vamos analisar a história deste problema e algumas soluções propostas.

Os alicerces do gerenciamento de recursos do .NET são a interface IDisposable e o bloco Using. A menos de objetos CLR, o ciclo de vida de tudo no mundo .NET é gerenciado utilizando-se estas ferramentas. Então, a pergunta que vem a mente é como a Microsoft conseguiu fracassar com isso no framework WCF.

O primeiro problema com os clientes de serviços WCF é que os métodos Close/Dispose podem lançar uma exceção. Sendo uma violação clara dos Princípios de Projeto do Framework e dos contratos IDisposable, isso faz com que o método Dispose seja inseguro ao ser chamado de um block Finally.

Pior ainda, há uma chance de que os métodos Close/Dispose deixem uma conexão aberta se o método Abort não for chamado. Se muitas forem deixadas abertas, isso pode levar a aplicação a ter problemas de performance e instabilidades.

Em uma mensagem em um newsgroup que data de 2006, Brian McNamara nos conta a história real por trás da falha de projeto.

A interface ICommunicationObject (da qual ServiceHost, ClientBase, IChannel, IChannelFactory e IChannerListener derivam) sempre teve dois métodos para destruir um objeto: (a) Close e (b) Abort. A semântica sugere que se você quer desativar de modo graceful, chame Close. Do contrário, para desativar de modo ungraceful, chame Abort.

Como conseqüência, o método Close() recebe um Timeout, possui uma versão assíncrona (já que ele pode bloquear a execução do programa) e pode lançar exceções. As exceções documentadas que o método pode lançar são CommunicationException (da qual CommunicationObjectFaultedException é uma subclasse) e TimeoutException.

O método Abort(), por outro lado, não deveria bloquear a execução (ou lançar alguma exceção esperada) e, desta forma, não possui um parâmetro de timeout e nem uma versão assíncrona.

Estes dois conceitos seguiram firmes da introdução do Indigo até hoje. Até aqui, tudo bem.

Na sua encarnação original, ICommunicationObject : IDisposable. Sendo uma interface de marcação, achamos que seria útil notificar os usuários que eles deveriam liberar o objeto o mais rápido possível. Aí que os problemas começaram.

Até o Beta 1, nós tínhamos Dispose() == Abort(). Parte da razão era que o Dispose() deveria fazer o mínimo necessário para realizar a limpeza. Esse foi possivelmente a maior reclamação que ouvimos no Beta 1. Os usuários iriam colocar seus channels em um block using() e qualquer mensagem em cache esperando para ser enviada seria perdida. Transações não receberiam commit, sessões receberiam ACK etc.

Para atender este feedback nós mudamos o comportamento para que Dipose() ~= Close(). Nós sabíamos que lançar exceções causa problemas (algumas delas estão listadas neste tópico do newsgroup), então fizemos que o Dispose tentasse ser esperto. Isto é, se não estivéssemos no estado Opened, nós iríamos internamente chamar o Abort(). Isso sozinho tem seus próprios problemas, o principal sendo que você não pode inferir nada do ponto de vista de confiabilidade sobre o sistema. O Dispose ainda pode lançar exceções, mas ele não irá te notificar _sempre_ que alguma coisa errada aconteceu. Eventualmente tomamos a decisão de que precisávamos tirar o IDisposable do ICommunicationObject. Depois de muita discussão, o IDisposable permaneceu no ServiceHost e no ClientBase. A teoria era que para a maioria dos usuários, não há problemas de um método Dispose lançar exceções, pois eles preferem contar com a conveniência do bloco using() e da indicação que o objeto deve ser liberado o mais rápido possível. Você poderia questionar (e alguns de nós questionamos) que nós também deveríamos ter removido a interface destas duas classes, mas para bem ou para mal, é nesta situação que nós estamos hoje. É uma questão que nunca vai agradar a todos, então nos nossos exemplos do SDK nós precisamos apoiar melhores práticas, que são utilizar o paradigma try { Close } / catch { Abort }.

Alternativas

Steve Smith propôs um extension method chamado CloseConnection. Este método, chamado de um bloco Finally no lugar do Close, encapsula a lógica Close/Abort.

O usuário bog1978 do newsgroup sugere utilizar o suporte a expressões lambda do C# para criar sua próprioa estrutura tipo Using. Este método recebe um novo objeto cliente e um método anônimo com o mesmo código que um bloco Using normal teria.

Por fim, existe a classe WCF Service Proxy Helper do Erwyn Van Der Meer. Os usuários a criam ao invés das classes proxy normais e ela fará a coisa certa ao fechar a conexão. Assim que é criada, ela irá construir o proxy automaticamente, expondo-o através de uma propriedade somente de leitura.

Avalie esse artigo

Relevância
Estilo/Redação

Olá visitante

Você precisa cadastrar-se no InfoQ Brasil ou para enviar comentários. Há muitas vantagens em se cadastrar.

Obtenha o máximo da experiência do InfoQ Brasil.

Dê sua opinião

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Receber mensagens dessa discussão
Comentários da comunidade

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Receber mensagens dessa discussão

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Receber mensagens dessa discussão

Dê sua opinião
Feedback geral
Bugs
Publicidade
Editorial
Marketing
InfoQ Brasil e todo o seu conteúdo: todos os direitos reservados. © 2006-2016 C4Media Inc.
Política de privacidade
BT

We notice you’re using an ad blocker

We understand why you use ad blockers. However to keep InfoQ free we need your support. InfoQ will not provide your data to third parties without individual opt-in consent. We only work with advertisers relevant to our readers. Please consider whitelisting us.