I have been struggling with WCF Proxies. What is the correct way to Dispose a WCF Proxy? The answer is not trivial.
System.ServiceModel.ClientBase violates Microsoft's own Dispose-pattern
System.ServiceModel.ClientBase<TChannel>
does implement IDisposable
so one must assume that it should be disposed or used in a using
-block. These are best practices for anything disposable. The implementation is explicit, however, so one does have to explicitly cast ClientBase
instances to IDisposable
, clouding the issue.
The biggest source of confusion, however, is that calling Dispose()
on ClientBase
instances that faulted, even channels that faulted because they never opened in the first place, will result in an exception being thrown. This, inevitably, means that the meaningful exception explaining the fault is immediately lost when the stack unwinds, the using
scope ends and Dispose()
throws a meaningless exception saying that you can't dispose a faulted channel.
The above behaviour is anathema to the dispose pattern which states that objects must be tolerant of multiple explicit calls to Dispose()
. (see http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx, "...allow the Dispose(bool)
method to be called more than once. The method might choose to do nothing after the first call.")
With the advent of inversion-of-control, this poor implementation becomes a real problem. I.O.C. containers (specifically, Ninject) detect the IDisposable
interface and call Dispose()
explicitly on activated instances at the end of an injection scope.
Solution: Proxy ClientBase and Intercept calls to Dispose()
My solution was to proxy ClientBase
by subclassing System.Runtime.Remoting.Proxies.RealProxy
and to hijack or intercept calls to Dispose()
. My first replacement for Dispose()
went something like this:
if (_client.State == CommunicationState.Faulted) _client.Abort();
else ((IDisposable)_client).Dispose();
(Note that _client
is a typed reference to the target of the proxy.)
Problems with NetTcpBinding
I thought that this had nailed it, initially, but then I discovered a problem in production: under certain scenarios that were fiendishly difficult to reproduce, I found that channels using a NetTcpBinding
were not closing properly in the unfaulted case, even though Dispose
was being called on _client
.
I had an ASP.NET MVC Application using my proxy implementation to connect to a WCF Service using a NetTcpBinding
on the local network, hosted within a Windows NT Service on a service cluster with only one node. When I load-tested the MVC Application, certain endpoints on the WCF Service (which was using port-sharing) would stop responding after a while.
I struggled to reproduce this: the same components running across the LAN between two developer's machines worked perfectly; a console application hammering the real WCF endpoints (running on the staging service cluster) with many processes and many threads in each worked; configuring the MVC Application on the staging server to connect to the endpoints on a developer's machine worked under load; running the MVC Application on a developer's machine and connecting to the staging WCF endpoints worked. The last scenario only worked under IIS Express, however, and this was a breakthrough. The endpoints would sieze up when load-testing the MVC Application under full-fat IIS on a developer's machine, connecting to the staging service cluster.
Solution: Close the Channel
After failing to understand the problem and reading many, many pages of the MSDN and other sources that claimed the problem shouldn't exist at all, I tried a long-shot and changed my Dispose()
work-around to...
if (_client.State == CommunicationState.Faulted) _client.Abort();
else if (_client.State == CommunicationState.Opened)
{
((IContextChannel)_client.Channel).Close();
((IDisposable)_client).Dispose();
}
else ((IDisposable)_client).Dispose();
... and the problem stopped occurring in all test setups and under load in the staging environment!
Why?
Can anyone explain what might have been happening and why explicitly closing the Channel
before calling Dispose()
solved it? As far as I can tell, this shouldn't be necessary.
Finally, I return to the opening question: What is the correct way to Dispose a WCF Proxy? Is my replacement for Dispose()
adequate?
The issue, as far as I have been able to understand, is that calling
Dispose
disposes off the handle, but doesn't actually close the channel, which then holds on to the resources and then eventually times out.This is why your service stopped responding after a while during load testing: because the initial calls held on to resources longer than you thought they would, and later calls could then not avail those resources.
I came up with the following solution. The premise of the solution is that calling
Dispose
should be enough to dispose off the handle as well as close the channel. The additional benefit is that if the client ends up in a faulted state, it is recreated so that subsequent calls succeed.If
ServiceClient<TService>
is injected into another class via a dependency injection framework likeNinject
, then all resources will properly be released.NB: Please note that in the case of
Ninject
, the binding must define a scope, i.e., it must not be missing anInXyzScope
or be defined with anInTransientScope
. If no scope makes sense, then useInCallScope
.Here's what I came up with: