In this post I explore the subtle but disastrous consequences of expecting a using block to clean up your WCF channels.
NOTE: the examples show use of a generated proxy but the issue and solution applies to all ICommunicationObject including generated proxies (ClientBase<T>) as well as ChannelFactory and ChannelFactory<T>.
Many sleepless nights have been spent wondering why, regardless of any exception handling, the following code throws a CommunicationObjectFaultedException when .HelloWorld() throws.
Typical 'using' disposal pattern
using (WCFServiceClient c = new WCFServiceClient())
{
try
{
c.HelloWorld();
}
catch (Exception ex)
{
// handle the exception
}
}
Consider that when an exception occurs during the use of a channel it enters the Faulted state and, in this Faulted state, calling .Close() will throw a CommunicationObjectFaultedException.
The using statement, as you know, ensures that .Dispose() is called before the using block is closed. For channels, which typically have private .Dispose() methods, .Dispose() simply calls .Close(). Aha! Are you picking up what I am putting down?
The trap of the typical using disposal pattern illustrated:
using (WCFServiceClient c = new WCFServiceClient())
{
try
{
c.HelloWorld();
}
catch (Exception ex)
{
// You don't know it yet but your mellow has just been harshed.
// If you handle this exception and fall through you will still be cheerfully greeted with
// an unhandled CommunicationObjectFaultedException when 'using' tries to .Close() the client.
// If you throw or re-throw from here you will never see that exception, it is gone forever.
// buh bye.
// All you will get is an unhandled CommunicationObjectFaultedException
}
} // <-- here is where the CommunicationObjectFaultedException is thrown
The solution to this problem is to ensure that the channel can successfully transition to the Closed state upon closure of the using block. This is done by acknowledging the Faulted state by calling .Abort() from within your catch.
A proper using disposal pattern
using (WCFServiceClient client = new WCFServiceClient())
{
try
{
client.ThrowException();
}
catch (Exception ex)
{
// acknowledge the Faulted state and allow transition to Closed
client.Abort();
// handle the exception or rethrow, makes no nevermind to me, my
// yob is done ;-D
}
}
There are some scenarios where a the shape of your surrounding code does not lend itself to using a using block.
While the using block does have it's benefits, such as the scoping provided by the block, in the context of a channel all it does is call .Close() and we can do that easily enough.
Proper use of a channel without using
WCFServiceClient c = new WCFServiceClient();
try
{
c.HelloWorld();
}
catch
{
// acknowledge the Faulted state and allow transition to Closed
c.Abort();
// handle or throw
throw;
}
finally
{
c.Close();
}
There you have it, my take on the proper use and disposal of WCF channels.