Мне нравится создавать экземпляры моих клиентов службы WCF в блоке using
, поскольку он в значительной степени является стандартным способом использования ресурсов, реализующих IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
Но, как отмечено в этой статье MSDN, обертка WCF-клиента в блоке using
может маскировать любые ошибки, которые приводят к клиенту оставаясь в неисправном состоянии (например, тайм-аут или проблема связи). Короче говоря, когда вызывается Dispose(), запускается метод Close() клиента, но выдает ошибку, потому что он в неисправном состоянии. Исходное исключение затем маскируется вторым исключением. Нехорошо.
Предлагаемый обходной путь в статье MSDN заключается в том, чтобы полностью избежать использования блока using
и вместо этого создавать экземпляры ваших клиентов и использовать их примерно так:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
По сравнению с блоком using
, я считаю это уродливым. И много кода для записи каждый раз, когда вам нужен клиент.
К счастью, я нашел несколько других обходных решений, таких как этот для IServiceOriented. Вы начинаете с:
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();
}
}
}
}
Что позволяет:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
Это неплохо, но я не считаю это выразительным и легко понятным как блок using
.
Обходной путь, который я сейчас пытаюсь использовать, я впервые прочитал о blog.davidbarret.net. В основном вы переопределяете клиентский метод Dispose()
, где бы вы его ни использовали. Что-то вроде:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
Это, похоже, позволяет снова разрешить блок using
без опасности маскировки исключения с ошибкой.
Итак, есть ли какие-либо другие ошибки, которые я должен искать для использования этих обходных решений? Кто-нибудь придумал что-нибудь лучше?