Published by Marco on 9. May 2014 10:25:11
------------------------------------------------------------------------

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 : 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(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())
{
  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.