This is a repost from my personal site:
I was having a discussion with a colleague of mine regarding use of the
using
statement as it applies to WCF calls. I always wrap using
statements around any IDisposable
(except in a few cases, like instantiating the Unity container instance for the application). It turns out that this is sometimes wrong, which has caused some developers to suggest that it is always wrong. Interestingly, there is a way to make it always right. When is use of
using
dangerous?In the book Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition), the authors give the following advice when implementing
IDisposable
:AVOID throwing an exception from withinDispose(bool)
except under critical situations where the containing process has been corrupted (leaks, inconsistent shared state, etc.).
Unfortunately for us
using
fanatics, there are situations where exceptions are thrown from a call to Dispose, perhaps the most famous of which is System.ServiceModel.ClientBase<TModel>
, which serves as the base class for all WCF Client Proxy classes generated by the ServiceModel Metadata Utility Tool (Svcutil.exe). MSDN also has a article on "Avoiding Problems with the Using Statement" where the suggestion is to not use the using
statement when Dispose()
can throw.But
using
makes my code look pretty!Have no fear, there is a solution! Marc Gravell has a post where he discusses how some extension method magic can be used to still allow us to use our
using
statements even in these scenarios. I was unable to get some of the code on the post to work as-is, but I took that as an opportunity to enhance the sample with an implementation of the Dispose Pattern which is discussed on the MDSN "Implementing a Dispose Method" article .Step 1: The wrapper interface – improved.
This is nearly verbatim from Marc’s Post, with a little addition of the ExceptionHandler action. Since any exceptions thrown by calling Dispose on BaseObject (the underlying
IDisposable
instance) will be suppressed, this approach has gathered a small amount of criticism. Generally the criticism is not enough to prevent its validity and usefulness in practice; nevertheless the ExceptionHandler has been added to allow any calling code to supply an Action to be taken when an exception is thrown. The only downside that remains is that it is not possible to maintain the call stack in the exception details in the event that we want to re-throw the Exception./// <summary>
/// Handles something we want to dispose but which might blow up on us.
/// </summary>
/// <typeparam name="T">A type that likes to blowup during dispose</typeparam>
public interface IDisposableWrapper<T> : IDisposable
{
T BaseObject { get; }
Action<Exception> ExceptionHandler { get; set; }
}
/// Handles something we want to dispose but which might blow up on us.
/// </summary>
/// <typeparam name="T">A type that likes to blowup during dispose</typeparam>
public interface IDisposableWrapper<T> : IDisposable
{
T BaseObject { get; }
Action<Exception> ExceptionHandler { get; set; }
}
Step 2: The wrapper implementation:
/// <remarks>
/// This class not only implements IDisposableWrapper but also serves as an implementation of the BaseDisposable pattern.
/// </remarks>
public class DisposableWrapper<T> : IDisposableWrapper<T> where T : class, IDisposable
{
public T BaseObject { get; private set; }
public DisposableWrapper(T baseObject) { BaseObject = baseObject; }
public Action<Exception> ExceptionHandler { get; set; }
/// This class not only implements IDisposableWrapper but also serves as an implementation of the BaseDisposable pattern.
/// </remarks>
public class DisposableWrapper<T> : IDisposableWrapper<T> where T : class, IDisposable
{
public T BaseObject { get; private set; }
public DisposableWrapper(T baseObject) { BaseObject = baseObject; }
public Action<Exception> ExceptionHandler { get; set; }
private bool disposed = false;
/// <summary>
/// The default implementation for a DisposableWrapper.
/// </summary>
/// <param name="disposing">true if being called from Dispose(), false if being called from a Finalizer</param>
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
if (disposing)
{
if (BaseObject != null)
{
try
{
OnDispose();
}
catch(Exception e)
{
if (ExceptionHandler != null)
{
ExceptionHandler(e);
}
} // swallow…
}
disposed = true;
}
}
/// <summary>
/// The default implementation for a DisposableWrapper.
/// </summary>
/// <param name="disposing">true if being called from Dispose(), false if being called from a Finalizer</param>
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
if (disposing)
{
if (BaseObject != null)
{
try
{
OnDispose();
}
catch(Exception e)
{
if (ExceptionHandler != null)
{
ExceptionHandler(e);
}
} // swallow…
}
disposed = true;
}
}
protected virtual void OnDispose()
{
BaseObject.Dispose();
}
{
BaseObject.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Step 3: A
DisposableWrapper
just for our ClientBase
The Expected Exceptions article on MSDN states:
Code that calls a client communication method must catch theTimeoutException
andCommunicationException
. One way to handle such errors is to abort the client and report the communication failure.
public class ClientWrapper<TProxy> : DisposableWrapper<TProxy>
where TProxy : class, IDisposable, ICommunicationObject
{
public ClientWrapper(TProxy proxy) : base(proxy) { }
where TProxy : class, IDisposable, ICommunicationObject
{
public ClientWrapper(TProxy proxy) : base(proxy) { }
/// <summary>
/// specific handling for service-model
/// </summary>
protected override void OnDispose()
{
if (BaseObject.State == CommunicationState.Faulted)
{
BaseObject.Abort();
}
else
{
try
{
BaseObject.Close();
}
catch (TimeoutException)
{
BaseObject.Abort();
}
catch (CommunicationException)
{
BaseObject.Abort();
}
catch
{
BaseObject.Abort();
throw;
}
}
}
}
/// specific handling for service-model
/// </summary>
protected override void OnDispose()
{
if (BaseObject.State == CommunicationState.Faulted)
{
BaseObject.Abort();
}
else
{
try
{
BaseObject.Close();
}
catch (TimeoutException)
{
BaseObject.Abort();
}
catch (CommunicationException)
{
BaseObject.Abort();
}
catch
{
BaseObject.Abort();
throw;
}
}
}
}
Step 4: Extension Methods:
This is one part where Marc’s code didn’t exactly work for me. For some reason, the extension method for disposing the
ClientBase<T>
was not able to be called without a cast. I took advantage of having to rewrite the extension method to change the interface of the method to loosen the restrictions on type T
to be IDisposable
and an ICommunicationObject
instead of only for ClientBase
.public static class WCFExtensions
{
// core "just dispose it without barfing"
public static IDisposableWrapper<T> Wrap<T>(this T baseObject)
where T : class, IDisposable
{
return baseObject as IDisposableWrapper<T> ?? new DisposableWrapper<T>(baseObject);
}
{
// core "just dispose it without barfing"
public static IDisposableWrapper<T> Wrap<T>(this T baseObject)
where T : class, IDisposable
{
return baseObject as IDisposableWrapper<T> ?? new DisposableWrapper<T>(baseObject);
}
// specific handling for service-model
public static IDisposableWrapper<T> WrapProxy<T>(this T proxy)
where T : class, IDisposable, ICommunicationObject
{
return new ClientWrapper<T>(proxy);
}
}
public static IDisposableWrapper<T> WrapProxy<T>(this T proxy)
where T : class, IDisposable, ICommunicationObject
{
return new ClientWrapper<T>(proxy);
}
}
Step 5: Use
using
safely:Now we can safely wrap our code in a
using
statement:using(var clientWrapper = new MyWcfServiceClient().WrapProxy())
{
clientWrapper.ExceptionHandler = ex =>
{
Logger.LogError(ex);
}
var response = clientWrapper.BaseObject.PerformAction();
}
{
clientWrapper.ExceptionHandler = ex =>
{
Logger.LogError(ex);
}
var response = clientWrapper.BaseObject.PerformAction();
}