What happens when I close/abort a WCF channel/proxy?
What happens when I close/abort a WCF channel/proxy?
I'm trying to get a better understanding of what's going on when I use a WCF proxy. I'm having trouble understanding what happens when I close (or don't close) a proxy.
- What's going on when I call Close() or Abort() on a WCF proxy? What's the difference?
- How does it differ between types of bindings (like, a sessionless BasicHttpBinding vs. something sessionful)?
- Why can Close() throw in certain situations and why can it be a blocking operation?
Closing WCF client
A client has an inherited responsibility of gracefully closing the connection. It is always recommended to close a proxy client. If the binding between a client and a service is transport-layer sessionful, then closing a proxy is essential to tear down the connection between both parties. Service has a payload threshold defined for concurrent connections. If the number of concurrent connections goes above this threshold linearly then the overall service performance decreases exponentially. This is why it is crucial to dispose of the connection as soon as possible. Closing the proxy also notifies the service instance that it is no longer in use and may be collected by GC (subject to service instance management). If the client does not close a connection, it is still automatically torn down by WCF timeouts (found in the configuration files).
Aborting WCF client
In the situation where there is a fault in the service-client interaction, the objects on both ends are potentially totally broken. Thus using a proxy after the exception is not advised. Given the WCF binding use transport sessions, the client after a fault would not even be able to close it (if there was no transport layer session then the client could use or close the proxy, but this is not recommended as the configuration of sessions could change). So after a fault has happened the only safe operation is to abort a proxy.
Close is a synchronous operation, it can throw if the transport session has been damaged by a fault and it is a blocking operation until a confirmatory response from service is received (true for some bindings).
threshold linearly then the overall service performance decreases exponentially.
Can you please help to understand this point.Abort
does or, more importantly, what can go wrong ifAbort
is not called. Does it only matter if you're trying to re-use your proxy client instance, would not callingAbort
affect new instances, and/or is it a memory leak issue?https://gist.github.com/RobinHerbots/1308811
The Proper Use and Disposal of WCF Channels (or CommunicationObjectFaultedException ?!*#*$)
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 asChannelFactory
andChannelFactory<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) { // handle the exception } }
Consider that when an exception occurs during the use of a channel, it enters the
Faulted
state and, in thisFaulted
state, calling.Close()
will throw aCommunicationObjectFaultedException
.The
using
statement, as you know, ensures that.Dispose()
is called before the using block is closed. For channels, which typically haveprivate
.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 theusing
block. This is done by acknowledging theFaulted
state by calling.Abort()
from within yourcatch
, which actually does close the channel albeit abruptly. Any subsequent.Close()
is a NOOP.A proper
using
disposal patternusing (WCFServiceClient client = new WCFServiceClient()) { try { client.ThrowException(); } catch (Exception ex) { // acknowledge the Faulted state and 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 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 { // acknowledge the Faulted state and 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.
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)