Submit to Reddit      
  

ChannelAdam WCF Library — Version 1 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.

SampleServiceClient client = null;

try
{
    client = new SampleServiceClient();

    var response = client.SampleOperation(1234);

    // Do some business logic
}
catch (FaultException<MyCustomException>)
{
    // Do some business logic for this SOAP Fault Exception
}
catch (FaultException)
{
    // Do some business logic for this SOAP Fault Exception
}
catch (CommunicationException)
{
    // Catch this expected exception so it is not propagated further.
    // Perhaps write this exception out to log file for gathering statistics...
}
catch (TimeoutException)
{
    // Catch this expected exception so it is not propagated further.
    // Perhaps write this exception out to log file for gathering statistics...
}
catch (Exception)
{
    // An unexpected exception that we don't know how to handle.
    // Perhaps write this exception out to log file for support purposes...
    throw;
}
finally
{
    // This will:
    // - be executed if any exception was thrown above in the 'try' (including ThreadAbortException); and
    // - ensure that CloseOrAbortServiceChannel() itself will not be interrupted by a ThreadAbortException
    //   (since it is executing from within a 'finally' block)
    CloseOrAbortServiceChannel(client);

    // Unreference the client
    client = null;
}


private void CloseOrAbortServiceChannel(ICommunicationObject communicationObject)
{
    bool isClosed = false;

    if (communicationObject == null || communicationObject.State == CommunicationState.Closed)
    {
        return;
    }

    try 
    {
        if (communicationObject.State != CommunicationState.Faulted)
        {
            communicationObject.Close();
            isClosed = true;
        }
    }
    catch (CommunicationException)
    {
        // Catch this expected exception so it is not propagated further.
        // Perhaps write this exception out to log file for gathering statistics...
    }
    catch (TimeoutException)
    {
        // Catch this expected exception so it is not propagated further.
        // Perhaps write this exception out to log file for gathering statistics...
    }
    catch (Exception)
    {
        // An unexpected exception that we don't know how to handle.
        // Perhaps write this exception out to log file for support purposes...
        throw;
    }
    finally
    {
        // If State was Faulted or any exception occurred while doing the Close(), then do an Abort()
        if (!isClosed)
        {
            AbortServiceChannel(communicationObject);
        }
    }
}


private static void AbortServiceChannel(ICommunicationObject communicationObject)
{
    try
    {
        communicationObject.Abort();
    }
    catch (Exception)
    {
        // An unexpected exception that we don't know how to handle.
        // If we are in this situation:
        // - we should NOT retry the Abort() because it has already failed and there is nothing to suggest it could be successful next time
        // - the abort may have partially succeeded
        // - the actual service call may have been successful
        //
        // The only thing we can do is hope that the channel's resources have been released.
        // Do not rethrow this exception because the actual service operation call might have succeeded
        // and an exception closing the channel should not stop the client doing whatever it does next.
        //
        // Perhaps write this exception out to log file for gathering statistics and support purposes...
    }
}

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.



comments powered by Disqus