Safe disposal of WCF proxies

The issue has been known for a long time: you cannot safely dispose a WCF proxy that inherits from ClientBase<T> with a using statement, because the dispose method may throw.  For more information, see http://www.google.com/search?q=wcf+proxy+dispose.

However, I’ve always found most solutions out there to be too complex, too cumbersome, or both. Some of them even seek to replace the Visual Studio (or svcutil.exe) generated proxy by handwritten code, in which case you loose the convenience of the “Add Service Reference” dialog and the automatic configuration file generation. I wanted a simple but effective solution that does not replace the Visual Studio code generation, and is easy to use at the same time.

I came up with a simple extension method, that you use as follows:

var proxy = new SomeServiceClient();
using (proxy.SafeDisposer())
{
    // use the proxy here
}

Here’s the code:

namespace U2UConsult.ServiceModel
{
    using System;
    using System.Diagnostics;
    using System.ServiceModel;

    public static class ClientBaseExtensions
    {
        private static readonly TraceSource traceSource = new TraceSource("U2UConsult.ServiceModel");

        public static IDisposable SafeDisposer<T>(this ClientBase<T> proxy) where T : class
        {
            return new SafeDisposerProxy<T>(proxy);
        }

        private sealed class SafeDisposerProxy<T> : IDisposable where T : class
        {
            private ClientBase<T> proxy;

            public SafeDisposerProxy(ClientBase<T> proxy)
            {
                this.proxy = proxy;
            }

            public void Dispose()
            {
                if (this.proxy != null)
                {
                    if (this.proxy.State == CommunicationState.Opened)
                    {
                        try
                        {
                            this.proxy.Close();
                        }
                        catch (Exception ex)
                        {
                            ClientBaseExtensions.traceSource.TraceEvent(
                                TraceEventType.Error, 
                                0, 
                                "Could not close client proxy, reason: {0}", 
                                ex.Message);
                            this.proxy.Abort();
                        }
                    }
                    else
                    {
                        this.proxy.Abort();
                    }

                    this.proxy = null;
                }
            }
        }
    }
}

Enjoy!