How Safe is the Using Block?
I’m sure there are a lot of people out there who think of the using block as their security blanket. You can just wrap anything that has a close or dispose method on it in a using block, and everything is handled the way it should be, and automagically. Well, this isn’t always the case.
While doing some WCF work using the System.Component.ClientBase<TChannel>, I was running into a few issues. I was creating client proxy classes that inherited from ClientBase<TChannel>, and when using these, I would just wrap them in a using block.
using( var proxy = new MyProxy() ) { // Do some work. }
This seems like what you’re supposed to do, but in this case, it’s not. I ran across an article on MSDN “Avoiding Problems with the Using Statement” that explains why you can’t use the using block. Basically, the Close() method on the proxy can throw exceptions, such as a timeout, and if this occurs, you need to call Abort() instead.
Here is the correct way to handle a proxy:
var proxy = new MyProxy(); try { // Do some work. proxy.Close(); } catch( CommunicationException ex ) { proxy.Abort(); } catch( TimeoutException ex ) { proxy.Abort(); } catch( Exception ex ) { proxy.Abort(); throw; }
EDIT:
After doing this a couple times, it gets pretty repetitive. I ended up creating a base proxy class.
public abstract class ProxyBase<TChannel> : ClientBase<TChannel>, IDisposable where TChannel : class
Then I implemented IDisposable.
protected void CheckDisposed() { if( disposed ) { throw new ObjectDisposedException( GetType().Name ); } } public void Dispose() { Dispose( true ); GC.SuppressFinalize( this ); } protected virtual void Dispose( bool disposing ) { if( !disposed ) { if( disposing ) { try { base.Close(); } catch( CommunicationException ex ) { base.Abort(); } catch( TimeoutException ex ) { base.Abort(); } catch( Exception ex ) { base.Abort(); throw; } } disposed = true; } }
Now we can wrap our proxy in a using block again, and we get our security blanket back.