I like instantiating my WCF service clients within a using block as it's pretty much the standard way to use resources that implement IDisposable:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
But, as noted in this MSDN article, wrapping a WCF client in a using block could mask any errors that result in the client being left in a faulted state (like a timeout or communication problem). Long story short, when Dispose() is called, the client's Close() method fires, but throws and error because it's in a faulted state. The original exception is then masked by the second exception. Not good.
The suggested workaround in the MSDN article is to completely avoid using a using block, and to instead instantiate your clients and use them something like this:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
Compared to the using block, I think that's ugly. And a lot of code to write each time you need a client.
Luckily, I found a few other workarounds, such as this one on IServiceOriented. You start with:
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();
}
}
}
}
Which then allows:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
}
That's not bad, but I don't think it's as expressive and easily understandable as the using block.
The workaround I'm currently trying to use I first read about on blog.davidbarret.net. Basically you override the client's Dispose() method wherever you use it. Something like:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
This appears to be able to allow the using block again without the danger of masking a faulted state exception.
So, are there any other gotchas I have to look out for using these workarounds? Has anybody come up with anything better?