Posts
32
Comments
106
Trackbacks
0
WCF Clients and the "Broken" IDisposable Implementation

I've been involved in a lengthy discussion recently at work involving the IDisposable implementation in WCF clients.

The topic is a complex one, potentially involving discussion of fault propagation strategies, general IDisposable patterns, proxy lifetime patterns, and the like.  However, for the purposes of this post, I'm only interested in the implementation details of IDisposable in WCF clients (specifically, System.ServiceModel.ClientBase<T>).

Props to a very respected co-worker of mine, Chris Rolon, for the following question:  What's wrong with the following code snippet (where SandboxServiceClient is a WCF client)?

using (sandbox.SandboxServiceClient serviceClient = new 
sandbox.SandboxServiceClient()) {

serviceClient.HelloWorld(name);

}

At first glance, one would think, "nothing".  And you could very well argue that many, many developers out there would be comfortable with it, oblivious to the potential pitfall contained within.  The using statement is an implicit IDisposable pattern, and if you're not intimately familiar with the using statement, simply know that the IDisposable interface Dispose method is called at the end of the using statement to ensure that resources are cleaned up.

If you use Reflector to examine the native behavior of the ClientBase<T> class (the base class of svcutil.exe-generated WCF client proxies), you see that the IDisposable implementation looks like:

void IDisposable.Dispose() {
this.Close();
}

"Fine", you say.  No problem.  Ahhh, but let's assume that the service throws an exception during the course of fulfilling your request, and let's assume the service (a .Net-based service) throws a native .Net exception type.  The .Net WCF infrastructure is smart enough to translate that exception into a fault.  But here's the kicker.  In certain situations (default out of the box wsHttpBinding being one of them), receiving a fault exception puts the client into a faulted state.

Guess what happens when you call this.Close() on a faulted ClientBase<T>?  It throws an exception.

Bad, bad, bad.

So, in the scenario above (the using statement), when the Dispose method is called at the end of the using statement, the Dispose method throws an exception.  The net result is that the original exception (the fault exception) is lost.

Bad, bad, bad.

So, how do you remedy the problem?  There are actually a couple different ways, but to solve the dilemma of the 'broken' IDisposable implementation, you need to check the state of the client to know whether you call .Close, or in the case of a faulted client, the .Abort method.  You can do this by avoiding the using statement altogether, and instantiating a client, performing your call, and then checking the status via if/then to know whether to call .Abort or .Close.

The slightly more elegant solution (IMHO), and the one proposed by Chris Rolon, was to override the default IDisposable behavior of the scvutil.exe-generated class.  Because this class is generated as partial, you can provide another partial implementation such as:

public partial class SandboxServiceClient : IDisposable
{

void IDisposable.Dispose() {

if (this.State == CommunicationState.Faulted) {
this.Abort();
} else {
this.Close();
}

}

}

With the fix implemented, faults occurring in the using statement are correctly propagated up the call stack, and are not in danger of being overwritten by a faulty IDisposable implementation.

As I mentioned above, there are other factors that can influence this scenario.  Implementing an exception shielding and propogation pattern on the service side via expected typed fault exceptions eliminates this issue, as the client behaves as expected when the interface declares (and the client receives) a typed fault.  Incidentally, using basicHttpBinding (and possibly others) prevents this issue from happening.  Some have also stated that a using statement is completely silly, as WCF clients are expensive to construct, and should be kept around for the lifetime of the application (and thus, the issue becomes more one of managing the state of the client, resetting it appropriately so that's it ready for the next call).

Regardless, a widely known pattern such as the using statement -- that's often suggested as "best practice" for logically cleaning up resources once you're done with them -- turns out to be a pitfall in this case.  But, knowing what's going on under the hood, we can adapt to correct the faulty behavior, and continue coding the way we're comfortable with.

posted on Thursday, November 22, 2007 8:58 PM Print
Comments
Gravatar
# re: WCF Clients and the "Broken" IDisposable Implementation
Wiebe Tijsma
12/28/2007 4:20 AM
Thanks for this, I was wondering who else was annoyed by this "breaking" behavior.

What I did was create a delegate and a method in the partial ServiceClientClass, then you will also allow you to control the instantiation and lifetime of the service outside your controller classes:

public partial class ContractsServiceClient

{
public static void UsingService(UseServiceHandler handler)
{
using (ContractsServiceClient _service = new ContractsServiceClient())
{
try
{
handler(_service);
}
catch (Exception)
{
_service.Abort();
throw;
}
}
}

public delegate void UseServiceHandler(IContractsService service);

}

then you can call your service like this from your controller classes:
ContractsServiceClient.UsingService(delegate(IContractsService service)
{
... service
}

Bit of a hack maybe :)
Gravatar
# re: WCF Clients and the "Broken" IDisposable Implementation
Samuel Jack
1/24/2008 4:02 AM
Thanks for writing this up. I'd been having the same problem and was looking around for an elegant solution, and using a partial class against the generated proxies fits the bill.
Gravatar
# re: WCF Clients and the "Broken" IDisposable Implementation
Suchmaschinenoptimierung Alex
5/9/2008 8:33 AM
Thank You very much for your article, really great.
Gravatar
# re: WCF Clients and the "Broken" IDisposable Implementation
David Barrett
10/1/2008 1:36 PM
Jesse Ezell over at iServiceOriented.com (a great site, by the way, for SOA-type stuff) presents another very elegant, generic-patterned approach to solving this problem. See his solution at http://www.iserviceoriented.com/blog/post/Indisposable+-+WCF+Gotcha+1.aspx

Thanks, Jesse!
Gravatar
# re: WCF Clients and the "Broken" IDisposable Implementation
Charles
10/25/2008 3:40 PM
Thank you very very much!
Gravatar
# re: WCF Clients and the "Broken" IDisposable Implementation
Laksh Parab
7/29/2009 1:39 PM
the article says make a call to close() inside finally. But according to my understanding when you call Close() method, it makes a call all the way to server and that server call may fail. So it does not make sense calling Close() inside finally.
Can someone tell me if im correct or wrong

Gravatar
# re: WCF Clients and the "Broken" IDisposable Implementation
Myles Johnson
10/26/2009 8:34 PM
What about async calls? You can't close the channel down until the callback. How would you handle that with your pattern?
Gravatar
# re: WCF Clients and the "Broken" IDisposable Implementation
David Barrett
10/26/2009 9:31 PM
Myles, this issue isn't directly a function of sync vs. async. In an async pattern, you wouldn't typically use the 'using' pattern because chances are you need to maintain the client to receive the callback. Regardless, you're advised to still call Dispose() on the client after you're done with it. In that case, calling Dispose() on a faulted channel still throws an exception. So the advice still holds, sync or async.

Post Comment

Title *
Name *
Email
Comment *  
 
Comment and opinions are my own and do not reflect on my company in any way.
Tag Cloud