Dealing with improper disposal in WCF clients

  Subscribe
5/9/2014 - Marco (updated on 11/13/2017)

There's an old problem in generated WCF clients in which the Dispose() method calls Close() on the client irrespective of whether there was a fault. If there was a fault, then the method should call Abort() instead. Failure to do so causes another exception, which masks the original exception. Client code will see the subsequent fault rather than the original one. A developer running the code in debug mode will have be misled as to what really happened.

You can see WCF Clients and the "Broken" IDisposable Implementation by David Barrett for a more in-depth analysis, but that's the gist of it.

This issue is still present in the ClientBase implementation in .NET 4.5.1. The linked article shows how you can add your own implementation of the Dispose() method in each generated client. An alternative is to use a generic adaptor if you don't feel like adding a custom dispose to every client you create.1

**public class** SafeClient<T> : IDisposable
  **where** T : ICommunicationObject, IDisposable
{
  **public** SafeClient(T client)
  {
    **if** (client == **null**) { **throw new** ArgumentNullException("client"); }

    Client = client;
  }
  
  **public** T Client { **get**; **private set**; }

  **public void** Dispose()
  {
    Dispose(**true**);
    GC.SuppressFinalize(**this**);
  }

  **protected virtual void** Dispose(**bool** disposing)
  {
    **if** (disposing)
    {
      **if** (Client != **null**)
      {
        **if** (Client.State == CommunicationState.Faulted) 
        {
          Client.Abort();
        }
        **else**
        {
          Client.Close();
        }

        Client = **default**(T);
      }
    }
  }  
}

To use your WCF client safely, you wrap it in the class defined above, as shown below.

**using** (**var** safeClient = **new** SafeClient<SystemLoginServiceClient>(**new** SystemLoginServiceClient(...)))
{
  **var** client = safeClient.Client;
  // Work with "client"
}

If you can figure out how to initialize your clients without passing parameters to the constructor, you could slim it down by adding a "new" generic constraint to the parameter T in SafeClient and then using the SafeClient as follows:

**using** (**var** safeClient = **new** SafeClient<SystemLoginServiceClient>())
{
  **var** client = safeClient.Client;
  // Work with "client"
}


  1. The code included in this article is a sketch of a solution and has not been tested. It does compile, though.

Sign up for our Newsletter