I struggled with WCF Proxies. What is the correct way to recycle a WCF proxy? The answer is not trivial.
System.ServiceModel.ClientBase violates corporation's own Dispose pattern
System.ServiceModel.ClientBase<TChannel> implements IDisposable , so you need to assume that it should be deleted or used in the using block. These are best practices for one-time use. However, the implementation is explicit, so you must explicitly point the ClientBase instances to IDisposable , obscuring the problem.
However, the biggest confusion is that calling Dispose() on ClientBase instances that fail, even channels that were blamed because they never opened in the first place, will result in an exception being thrown. This inevitably means that a meaningful exception explaining the error is immediately lost when the packet breaks up, the using area ends and Dispose() throws a meaningless exception, saying that you cannot destroy the failed channel.
The above behavior is the anathema of the dispose pattern, which indicates that objects should 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 may decide to do nothing after the first call. ")
With the advent of control inversion, this poor implementation becomes a real problem. IOC containers (specifically Ninject) detect the IDisposable interface and call Dispose() explicitly on the activated instances at the end of the injection area.
Solution: Proxy client base and call pickup for Dispose ()
My solution was to proxy ClientBase by subclassing System.Runtime.Remoting.Proxies.RealProxy and to capture 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 proxy target.)
Problems with NetTcpBinding
I thought it nailed it at first, but then I discovered a production problem: in certain scenarios that were brutally difficult to play, I found that channels using NetTcpBinding did not close properly in case of accident, even if Dispose is called on _client .
I had an ASP.NET MVC application using my proxy implementation to connect to the WCF service using NetTcpBinding on a LAN hosted by a Windows NT service in a service cluster with only one node. When I tested the MVC application, some endpoints in the WCF service (which used port sharing) stopped responding after a while.
I tried my best to reproduce this: the same components working on the local network between the two development machines worked fine; a console application that clogs real WCF endpoints (running on an intermediate service cluster) with many processes and many threads in each of them; Configuring an MVC application on an intermediate server to connect to endpoints on a development machine running under load running the MVC application on the developer's machine and connecting to intermediate WCF endpoints. However, the last scenario worked only in IIS Express, and it was a breakthrough. Endpoints will loop when testing the load of an MVC application in IIS full load mode on the developer's machine, connecting to a cluster of intermediate services.
Solution: Close the channel
After you didn’t understand the problem and read many, many MSDN pages and other sources that reported the problem should not exist at all, I tried to take a long shot and changed my Dispose() working environment ..
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 in all test installations and under load in the intermediate environment!
Why?
Can anyone explain what could have happened and why explicitly closing the Channel before calling Dispose() solved it? As far as I can tell, this is not necessary.
Finally, I return to the first question: What is the correct way to utilize the WCF proxy server? Is my replacement for Dispose() adequate?