March 05, 2010 3:37 AM by Daniel Chambers (last modified on March 05, 2010 3:49 AM)
While working with WCF for my part time job, I came across this page on MSDN that condemned the C# using block as unsafe to use when working with a WCF client. The problem is that the using block can silently swallow exceptions without you even knowing. To prove this, here’s a small sample:
public static void Main() { try { using (new CrashingDisposable()) { throw new Exception("Inside using block"); } } catch (Exception e) { Console.WriteLine("Caught exception: " + e.Message); } } private class CrashingDisposable : IDisposable { public void Dispose() { throw new Exception("Inside Dispose"); } }
The above program will write “Caught exception: Inside Dispose” to the console. But where did the “Inside using block” exception go? It was swallowed by the using block! How this happens is more obvious when you unroll the using block into the try/finally block that it really is (note the outer braces that limit crashingDisposable’s scope):
{ CrashingDisposable crashingDisposable = new CrashingDisposable(); try { throw new Exception("Inside using block"); } finally { if (crashingDisposable != null) ((IDisposable)crashingDisposable).Dispose(); //Dispose exception thrown here } }
As you can see, the “Inside using block” exception is lost entirely. A reference to it isn’t even present and the exception from the Dispose call is the one that gets thrown up.
So, how does this affect you in WCF? Well, when a WCF client is disposed it is closed, which may throw an exception. So if, while using your client object, you encounter an exception that escapes the using block, the client will be disposed and therefore closed, which could throw an exception that will hide the original exception. That’s just bad for debugging.
This is obviously undesirable behaviour, so I’ve written a construct that I’ve dubbed a “Safe Using Block” that stops the exception thrown in the using block from being lost. Instead, the safe using block gathers both exceptions together and throws them up inside an AggregateException (present in DigitallyCreated.Utilities.Bcl, but soon to be found in .NET 4.0) Here’s the above using block rewritten as a safe using block:
new CrashingDisposable().SafeUsingBlock(crashingDisposable => { throw new Exception("Inside using exception"); });
When this code runs an AggregateException is thrown that contains both the “Inside using exception” exception and the “Dispose” exception. So how does this work? Here’s the code:
public static void SafeUsingBlock<TDisposable>(this TDisposable disposable, Action<TDisposable> action) where TDisposable : IDisposable { try { action(disposable); } catch (Exception actionException) { try { disposable.Dispose(); } catch (Exception disposeException) { throw new DigitallyCreated.Utilities.Bcl.AggregateException(actionException, disposeException); } throw; } disposable.Dispose(); //Let it throw on its own }
SafeUsingBlock is defined as an extension method off any type that implements IDisposable. It takes an Action delegate, which represents the code to run inside the “using block”. Since the method uses generics, the Action delegate is handed the concrete type of IDisposable you created, not just an abstract IDisposable.
You can use a safe using block in a very similar fashion to a normal using block, except for one key thing: goto-style statements won’t work. So for example, you can’t use return, break, continue, etc inside the safe using block and expect it to affect the method outside. You must keep in mind that you’re writing inside a new anonymous method, not inside a code block.
SafeUsingBlock is part of DigitallyCreated Utilities, however, it is currently only available in the trunk repository, not as a part of a release. Once I’m done working on the documentation, I’ll put it out in a full release.