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 the 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, even though the exception is being swallowed.
Typical 'using' Disposal Pattern
using (WCFServiceClient c = new WCFServiceClient())
{
try
{
c.HelloWorld();
}
catch (Exception ex)
{
}
}
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)
{
}
}
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
, which actually does close the channel albeit abruptly. Any subsequent .Close()
is a NOOP.
A proper using
disposal pattern
using (WCFServiceClient client = new WCFServiceClient())
{
try
{
client.ThrowException();
}
catch (Exception ex)
{
client.Abort();
}
}
There are some scenarios where the shape of your surrounding code does not lend itself to using a using
block.
While the using
block does have its 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
{
c.Abort();
throw;
}
finally
{
c.Close();
}
There you have it, my take on the proper use and disposal of WCF channels.