Background

ChannelAdam WCF Library — Version 2 Documentation

Background

The Issues

The issues faced by .NET developers are explained in depth in the article How To Call WCF Services Properly, where I researched and highlighted the typical problems with using WCF clients (such as memory and connection leaks), and concluded that the most correct solution for consuming WCF services would:

  • Perform the Close/Abort pattern without a race condition;
  • Handle the situation when the service operation throws exceptions;
  • Handle the situations when both the Close and Abort methods throw exceptions; and
  • Handle asynchronous exceptions such as the ThreadAbortException.

Below is the verbose sample code that is required for correctly using a .NET WCF client, performing exception handling and correctly performing the Close/Abort pattern. Unfortunately, due to the way Microsoft designed WCF, if you want to use a WCF service client cleanly without connection or memory leaks, this is the type of extensive code that must be written for every service operation call.

  1SampleServiceClient client = null;
  2
  3try
  4{
  5    client = new SampleServiceClient();
  6
  7    var response = client.SampleOperation(1234);
  8
  9    // Do some business logic
 10}
 11catch (FaultException<MyCustomException>)
 12{
 13    // Do some business logic for this SOAP Fault Exception
 14}
 15catch (FaultException)
 16{
 17    // Do some business logic for this SOAP Fault Exception
 18}
 19catch (CommunicationException)
 20{
 21    // Catch this expected exception so it is not propagated further.
 22    // Perhaps write this exception out to log file for gathering statistics...
 23}
 24catch (TimeoutException)
 25{
 26    // Catch this expected exception so it is not propagated further.
 27    // Perhaps write this exception out to log file for gathering statistics...
 28}
 29catch (Exception)
 30{
 31    // An unexpected exception that we don't know how to handle.
 32    // Perhaps write this exception out to log file for support purposes...
 33    throw;
 34}
 35finally
 36{
 37    // This will:
 38    // - be executed if any exception was thrown above in the 'try' (including ThreadAbortException); and
 39    // - ensure that CloseOrAbortServiceChannel() itself will not be interrupted by a ThreadAbortException
 40    //   (since it is executing from within a 'finally' block)
 41    CloseOrAbortServiceChannel(client);
 42
 43    // Unreference the client
 44    client = null;
 45}
 46
 47
 48private void CloseOrAbortServiceChannel(ICommunicationObject communicationObject)
 49{
 50    bool isClosed = false;
 51
 52    if (communicationObject == null || communicationObject.State == CommunicationState.Closed)
 53    {
 54        return;
 55    }
 56
 57    try 
 58    {
 59        if (communicationObject.State != CommunicationState.Faulted)
 60        {
 61            communicationObject.Close();
 62            isClosed = true;
 63        }
 64    }
 65    catch (CommunicationException)
 66    {
 67        // Catch this expected exception so it is not propagated further.
 68        // Perhaps write this exception out to log file for gathering statistics...
 69    }
 70    catch (TimeoutException)
 71    {
 72        // Catch this expected exception so it is not propagated further.
 73        // Perhaps write this exception out to log file for gathering statistics...
 74    }
 75    catch (Exception)
 76    {
 77        // An unexpected exception that we don't know how to handle.
 78        // Perhaps write this exception out to log file for support purposes...
 79        throw;
 80    }
 81    finally
 82    {
 83        // If State was Faulted or any exception occurred while doing the Close(), then do an Abort()
 84        if (!isClosed)
 85        {
 86            AbortServiceChannel(communicationObject);
 87        }
 88    }
 89}
 90
 91
 92private static void AbortServiceChannel(ICommunicationObject communicationObject)
 93{
 94    try
 95    {
 96        communicationObject.Abort();
 97    }
 98    catch (Exception)
 99    {
100        // An unexpected exception that we don't know how to handle.
101        // If we are in this situation:
102        // - we should NOT retry the Abort() because it has already failed and there is nothing to suggest it could be successful next time
103        // - the abort may have partially succeeded
104        // - the actual service call may have been successful
105        //
106        // The only thing we can do is hope that the channel's resources have been released.
107        // Do not rethrow this exception because the actual service operation call might have succeeded
108        // and an exception closing the channel should not stop the client doing whatever it does next.
109        //
110        // Perhaps write this exception out to log file for gathering statistics and support purposes...
111    }
112}

ChannelAdam WCF Library Makes It Easy

The ChannelAdam WCF Library is an API for working with WCF clients, that significantly reduces the amount of that code that developers have to write. It allows you to forget about the complex subtleties of WCF clients, channels and communication states, and lets you just use your service client in a way that is very similar to how you probably are doing it now - but without the leaks!

When you use the ChannelAdam WCF Library’s ServiceConsumer, you know that you can keep calling service operations through the ServiceConsumer and it will ‘just work’ and not leak connections or memory - no matter what happens!

The ChannelAdam WCF Library’s ServiceConsumer hides the inner complexity of service channels. For example, if a service channel / communication object / service client moves into a Fault state, the ServiceConsumer will correctly perform the Close/Abort pattern and dispose of the channel. The next time a request is made through the ServiceConsumer, a new channel is created under the covers to seamlessly process the request.

Please leave below any comments, feedback or suggestions, or alternatively contact me on a social network.

comments powered by Disqus