Gosto de instanciar meus clientes de serviço WCF dentro de um using
bloco, já que é praticamente a maneira padrão de usar recursos que implementam IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
Mas, conforme observado neste artigo do MSDN , agrupar um cliente WCF em um using
bloco pode mascarar quaisquer erros que resultem no cliente sendo deixado em um estado com falha (como um tempo limite ou um problema de comunicação). Para encurtar a história, quando Dispose () é chamado, o método Close () do cliente é acionado, mas gera um erro porque está em um estado com falha. A exceção original é mascarada pela segunda exceção. Não é bom.
A solução sugerida no artigo do MSDN é evitar completamente o uso de um using
bloco e instanciar seus clientes e usá-los da seguinte maneira:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
Comparado ao using
bloco, acho isso feio. E muito código para escrever sempre que você precisar de um cliente.
Felizmente, encontrei algumas outras soluções alternativas, como esta no IServiceOriented. Você começa com:
public delegate void UseServiceDelegate<T>(T proxy);
public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
O que permite:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
Isso não é ruim, mas não acho que seja tão expressivo e facilmente compreensível quanto o using
bloco.
A solução alternativa que estou tentando usar foi a que li pela primeira vez no blog.davidbarret.net . Basicamente, você substitui o Dispose()
método do cliente onde quer que o use. Algo como:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
Parece ser capaz de permitir o using
bloco novamente sem o perigo de mascarar uma exceção de estado com falha.
Então, existem outras dicas que eu devo procurar ao usar essas soluções alternativas? Alguém sugeriu algo melhor?
Action<T>
vez deUseServiceDelegate<T>
. menor.Service<T>
pois complica o teste de unidade (como a maioria das coisas estáticas). Eu preferiria que não fosse estático, para poder ser injetado na classe que o está usando.Respostas:
Na verdade, embora eu tenha blogado (veja a resposta de Luke ), acho que isso é melhor do que meu invólucro descartável. Código típico:
(editar por comentários)
Como os
Use
retornos são nulos, a maneira mais fácil de lidar com os valores de retorno é através de uma variável capturada:fonte
public static TResult Use<TResult>(Func<T, TResult> codeBlock) { ... }
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
ehttps://devzone.channeladam.com/articles/2014/09/how-to-easily-call-wcf-service-properly/
ehttp://dzimchuk.net/post/wcf-error-helpers
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
Dada a escolha entre a solução preconizada pelo IServiceOriented.com e a solução preconizada pelo blog de David Barret , prefiro a simplicidade oferecida pela substituição do método Dispose () do cliente. Isso me permite continuar usando a instrução using () como seria de esperar de um objeto descartável. No entanto, como @Brian apontou, esta solução contém uma condição de corrida, na qual o Estado pode não sofrer falhas quando é verificado, mas pode ser no momento em que Close () é chamado. Nesse caso, a CommunicationException ainda ocorre.
Então, para contornar isso, empreguei uma solução que combina o melhor dos dois mundos.
fonte
success
sinalizador? Por que nãotry { Close(); } catch { Abort(); throw; }
?Close(); success = true;
? Eu não gostaria que fosse lançada uma exceção se pudesse abortá-la com sucesso no bloco finalmente. Eu só iria querer uma exceção lançada se o Abort () falhasse nesse caso. Dessa forma, a tentativa / captura ocultaria a exceção potencial de condição de corrida e ainda permitiria que você abortasse () a conexão no bloco final.Eu escrevi uma função de ordem superior para fazê-la funcionar corretamente. Usamos isso em vários projetos e parece funcionar muito bem. É assim que as coisas deveriam ter sido feitas desde o início, sem o paradigma "usar" ou mais.
Você pode fazer chamadas assim:
Isso é praticamente como você tem no seu exemplo. Em alguns projetos, escrevemos métodos auxiliares de tipo forte, por isso acabamos escrevendo coisas como "Wcf.UseFooService (f => f ...)".
Acho bastante elegante, considerando todas as coisas. Você encontrou algum problema em particular?
Isso permite que outros recursos bacanas sejam conectados. Por exemplo, em um site, o site é autenticado no serviço em nome do usuário conectado. (O site não possui credenciais por si só.) Ao escrever nosso próprio auxiliar de método "UseService", podemos configurar a fábrica de canais da maneira que queremos, etc. Também não somos obrigados a usar os proxies gerados - qualquer interface serve .
fonte
GetCachedFactory
método?Esta é a maneira recomendada pela Microsoft de lidar com chamadas de clientes WCF:
Para obter mais detalhes, consulte: Exceções esperadas.
Informações adicionais Muitas pessoas parecem estar fazendo essa pergunta no WCF que a Microsoft até criou um exemplo dedicado para demonstrar como lidar com exceções:
c: \ WF_WCF_Samples \ WCF \ Basic \ Client \ ExpectedExceptions \ CS \ client
Baixe o exemplo: C # ou VB
Considerando que existem muitos problemas envolvendo a instrução using , (aquecida?) Discussões e tópicos internos sobre esse assunto, não vou perder meu tempo tentando me tornar um cowboy de código e encontrar uma maneira mais limpa. Vou apenas absorver e implementar clientes WCF desta maneira detalhada (ainda que confiável) para meus aplicativos de servidor.
Falhas adicionais opcionais na captura
Muitas exceções derivam
CommunicationException
e não acho que a maioria dessas exceções deva ser tentada novamente. Examinei cada exceção no MSDN e encontrei uma pequena lista de exceções de nova tentativa (além dasTimeOutException
anteriores). Informe-me se perdi uma exceção que deve ser repetida.É certo que este é um pouco de código mundano para escrever. No momento, prefiro essa resposta e não vejo nenhum "hacks" nesse código que possa causar problemas no futuro.
fonte
"Hope this code wasn't important, because it might not happen."
ainda é executado ...Finalmente encontrei alguns passos sólidos em direção a uma solução limpa para esse problema.
O Codeplex possui um projeto chamado Exception Handling WCF Proxy Generator . Basicamente, instala uma nova ferramenta personalizada no Visual Studio 2008 e, em seguida, use essa ferramenta para gerar o novo proxy de serviço (Adicionar referência de serviço) . Possui algumas funcionalidades interessantes para lidar com canais com falha, tempos limite e descarte seguro. Há um excelente vídeo aqui chamado ExceptionHandlingProxyWrapper explicando exatamente como isso funciona.
Você pode usar a
Using
instrução novamente com segurança e, se o canal estiver com falha em qualquer solicitação (TimeoutException ou CommunicationException), o Wrapper reinicializará o canal com falha e tentará novamente a consulta. Se isso falhar, ele chamará oAbort()
comando e descartará o proxy e novamente a exceção. Se o serviço lançar umFaultException
código, ele será interrompido e o proxy será abortado com segurança, lançando a exceção correta conforme o esperado.fonte
Com base nas respostas de Marc Gravell, MichaelGG e Matt Davis, nossos desenvolvedores apresentaram o seguinte:
Exemplo de uso:
É o mais próximo possível da sintaxe "using", você não precisa retornar um valor fictício ao chamar um método nulo e pode fazer várias chamadas ao serviço (e retornar vários valores) sem precisar usar tuplas.
Além disso, você pode usar isso com
ClientBase<T>
descendentes em vez de ChannelFactory, se desejar.O método de extensão é exposto se um desenvolvedor desejar descartar manualmente um proxy / canal.
fonte
DisposeSafely
privado é certamente uma opção e evitaria confusão. Pode haver casos de uso em que alguém queira chamá-lo diretamente, mas não posso inventar um de imediato.https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
@Marc Gravell
Não seria bom usar isso:
Ou, a mesma coisa
(Func<T, TResult>)
no caso deService<IOrderService>.Use
Isso facilitaria o retorno de variáveis.
fonte
O que é isso?
Esta é a versão CW da resposta aceita, mas com (o que eu considero completo) Tratamento de exceção incluído.
A resposta aceita faz referência a este site que não existe mais . Para evitar problemas, incluo aqui as partes mais relevantes. Além disso, eu o modifiquei um pouco para incluir o tratamento de novas tentativas de exceção para lidar com esses tempos limite de rede irritantes.
Uso simples do cliente WCF
Depois de gerar seu proxy do lado do cliente, é tudo o que você precisa para implementá-lo.
ServiceDelegate.cs
Adicione este arquivo à sua solução. Não são necessárias alterações neste arquivo, a menos que você queira alterar o número de tentativas ou quais exceções você deseja manipular.
PS: Eu fiz deste post um wiki da comunidade. Não coletarei "pontos" desta resposta, mas prefiro que você a vote novamente se concordar com a implementação ou edite-a para torná-la melhor.
fonte
success == false
à declaração if finalAbaixo está uma versão aprimorada da fonte da pergunta e estendida para armazenar em cache várias fábricas de canais e tentar procurar o terminal no arquivo de configuração pelo nome do contrato.
Ele usa o .NET 4 (especificamente: contravariância, LINQ
var
):fonte
UseServiceDelegate<T>
vez deAction<T>
?Action<T>
funciona tão bem.Um wrapper como este funcionaria:
Isso deve permitir que você escreva códigos como:
Obviamente, o wrapper pode capturar mais exceções, se necessário, mas o princípio permanece o mesmo.
fonte
Dispose
um IChannel, ele pode gerar uma exceção se o canal estiver em um estado com falha, isso é um problema, pois a Microsoft especifica queDispose
nunca deve ser lançado. Portanto, o que o código acima faz é lidar com o caso quandoClose
lança uma exceção. SeAbort
joga, pode ser algo seriamente errado. Eu escrevi um post sobre isso em dezembro passado: blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapperUsei o proxy dinâmico do Castle para resolver o problema Dispose () e também implementei a atualização automática do canal quando ele estava em um estado inutilizável. Para usar isso, você deve criar uma nova interface que herda seu contrato de serviço e o IDisposable. O proxy dinâmico implementa essa interface e agrupa um canal WCF:
Gosto disso, pois você pode injetar serviços WCF sem que os consumidores precisem se preocupar com detalhes do WCF. E não há nenhum acréscimo adicionado como as outras soluções.
Dê uma olhada no código, é realmente muito simples: WCF Dynamic Proxy
fonte
Use um método de extensão:
fonte
Se você não precisar de IoC ou estiver usando um cliente gerado automaticamente (Referência de Serviço), poderá usar um wrapper para gerenciar o fechamento e deixar o GC aproveitar a base de clientes quando estiver em um estado seguro que não gerará nenhuma exceção. O GC chamará Dispose no serviço técnico, e isso chamará
Close
. Uma vez que já está fechado, não pode causar nenhum dano. Estou usando isso sem problemas no código de produção.Então, quando você estiver acessando o servidor, crie o cliente e use
using
no autodisconect:fonte
Sumário
Usando as técnicas descritas nesta resposta, é possível consumir um serviço WCF em um bloco using com a seguinte sintaxe:
É claro que você pode adaptar isso ainda mais para obter um modelo de programação mais conciso específico para sua situação - mas o ponto é que podemos criar uma implementação de
IMyService
reprenting do canal que implementa corretamente o padrão descartável.Detalhes
Todas as respostas dadas até agora abordam o problema de contornar o "bug" na implementação do canal WCF de
IDisposable
. A resposta que parece oferecer o modelo de programação mais conciso (permitindo que você use ousing
bloco para dispor de recursos não gerenciados) é esta - onde o proxy é modificado para implementarIDisposable
com uma implementação sem erros. O problema com essa abordagem é a manutenção - precisamos reimplementar essa funcionalidade para sempre usar o proxy. Em uma variação dessa resposta, veremos como podemos usar a composição, em vez da herança, para tornar essa técnica genérica.Primeira tentativa
Parece haver várias implementações para a
IDisposable
implementação, mas por uma questão de argumento, usaremos uma adaptação daquela usada pela resposta atualmente aceita .Armado com as classes acima, agora podemos escrever
Isso nos permite consumir nosso serviço usando o
using
bloco:Tornando este genérico
Tudo o que fizemos até agora é reformular a solução de Tomas . O que impede que esse código seja genérico é o fato de que a
ProxyWrapper
classe precisa ser reimplementada para cada contrato de serviço que desejamos. Agora, veremos uma classe que nos permite criar esse tipo dinamicamente usando IL:Com nossa nova classe auxiliar, agora podemos escrever
Observe que você também pode usar a mesma técnica (com pequenas modificações) para clientes gerados automaticamente que herdam
ClientBase<>
(em vez de usarChannelFactory<>
) ou se quiser usar uma implementação diferenteIDisposable
para fechar seu canal.fonte
Eu gosto desta maneira de fechar a conexão:
fonte
Eu escrevi uma classe base simples que lida com isso. Está disponível como um pacote NuGet e é bastante fácil de usar.
fonte
Por isso, permite escrever instruções de retorno bem:
fonte
Eu gostaria de adicionar a implementação do Service da resposta de Marc Gravell para o caso de usar o ServiceClient em vez do ChannelFactory.
fonte
Para os interessados, aqui está uma tradução em VB.NET da resposta aceita (abaixo). Refinei-o um pouco por questões de concisão, combinando algumas dicas de outras pessoas neste segmento.
Admito que não seja o tópico para as tags de origem (C #), mas como não consegui encontrar uma versão VB.NET dessa boa solução, presumo que outras pessoas também estejam procurando. A tradução do Lambda pode ser um pouco complicada, então eu gostaria de salvar alguém do problema.
Observe que essa implementação específica fornece a capacidade de configurar o
ServiceEndpoint
em tempo de execução.Código:
Uso:
fonte
Nossa arquitetura do sistema geralmente usa a estrutura do Unity IoC para criar instâncias do ClientBase, portanto não há como garantir que os outros desenvolvedores usem
using{}
blocos. Para torná-lo o mais à prova de idiotas possível, criei essa classe personalizada que estende o ClientBase e lida com o fechamento do canal ao dispor ou ao finalizar caso alguém não descarte explicitamente a instância criada pelo Unity.Também há coisas que precisam ser feitas no construtor para configurar o canal para credenciais personalizadas e outras coisas, então isso também está aqui ...
Então um cliente pode simplesmente:
E o chamador pode fazer qualquer um destes:
fonte
Consultei algumas respostas neste post e o personalizei de acordo com minhas necessidades.
Eu queria a capacidade de fazer algo com o cliente WCF antes de usá-lo para o
DoSomethingWithClient()
método.Aqui está a classe auxiliar:
E eu posso usá-lo como:
fonte
Eu tenho meu próprio wrapper para um canal que implementa Dispose da seguinte maneira:
Isso parece funcionar bem e permite que um bloco usando seja usado.
fonte
O ajudante a seguir permite chamar
void
métodos não nulos. Uso:A classe em si é:
fonte
Substitua Dispose () do cliente sem a necessidade de gerar uma classe de proxy baseada no ClientBase, também sem a necessidade de gerenciar a criação e o cache do canal ! (Observe que WcfClient não é uma classe ABSTRACT e se baseia no ClientBase)
fonte
Meu método para fazer isso foi criar uma classe herdada que implemente explicitamente IDisposable. Isso é útil para pessoas que usam a GUI para adicionar a referência de serviço (Adicionar referência de serviço). Acabei de soltar essa classe no projeto que faz a referência do serviço e a uso em vez do cliente padrão:
Nota: Esta é apenas uma implementação simples de descarte; você pode implementar uma lógica de descarte mais complexa, se desejar.
Em seguida, você pode substituir todas as chamadas feitas pelo cliente de serviço regular pelos clientes seguros, desta forma:
Gosto dessa solução, pois ela não exige que eu tenha acesso às definições de interface e posso usar a
using
instrução como seria de esperar, permitindo que meu código parecesse mais ou menos o mesmo.Você ainda precisará lidar com as exceções que podem ser lançadas conforme indicado em outros comentários neste tópico.
fonte
Você também pode usar a
DynamicProxy
para estender oDispose()
método. Dessa forma, você pode fazer algo como:fonte