Cleaning up WCF clients using Interception


If you have ever used the using statement to create a WCF client proxy before, you would have no doubt encountered problems of not getting your Service Exceptions propagating to the client. When exceptions are raised from service, your client proxy goes into a Faulted state, and attempting to close/dispose the proxy object (from the using statement) will result in WCF raising another Exception.

If you’re aware of this, you would have written code somewhat similar to this, to do proper cleanup.

SomeClient client = new SomeClient();

try
{
    client.SomeMethod();
    client.Close();
}
catch (CommunicationException)
{
    client.Abort();
}
catch (TimeoutException)
{
    client.Abort();
}
catch (Exception)
{
    client.Abort();
    throw;
}

I don’t particular like writing code like this, especially when I have to repeat it for ‘X’ number of methods for my service. That has provided me with the motivation to come up with a way to avoid repeating this lengthy code. In my previous blog talking about having Fun with Attributes, I briefly about cross-cutting your concerns, and in this post I’m doing to be demonstrating how to do this.

In Enterprise Library 4.1, there’s loads of powerful stuff in there, but I feel that there are insufficient samples and tutorials to help people like me learn and understand what it brings to the table. The Unity Application Block in Ent Lib 4.1 is a framework that provides Dependency Injection, Inversion of Control and Interception!

Interception allows a method to be intercepted and allows you to perform pre or post processing before it executes the actual method. This is useful in many situations, e.g. opening/closing a DB connection, and in our case, propagating Exceptions from WCF Services and performing the necessary cleanup. So let’s dig into the code.

You will first need to create a class that implements Microsoft.Practices.Unity.InterceptionExtension.ICallHandler, which is the handling class for our interception.

public class WcfCleanupHandler : ICallHandler
{
    public int Order { get; set; }

    #region ICallHandler Members

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        IProxy proxy = input.Target as IProxy;
        if (proxy == null)
        {
            return input.CreateExceptionMethodReturn(new Exception("input target is not of IProxy interface"));
        }
        proxy.Open();
        IMethodReturn msg = getNext()(input, getNext);
        if (msg.Exception != null)
        {
            proxy.Abort();
        }
        else
        {
            proxy.Close();
        }
        return msg;
    }

    #endregion
}

In the interception method, I do pre-processing by checking if the IMethodInvocation.Target object (the class we intercepted) is of type IProxy, which is an interface I created that provides the Contract for Opening, Aborting and Closing a WCF Client (we will see more of IProxy later). When we execute the getNext() delegate to execute the actual method, we get back an instance of IMethodReturn, which will either contain our return value or an Exception.

If the WCF Service fails with an Exception, we check for that using IMethodReturn.Exception Property and close the WCF client if an Exception is found. Otherwise the return value is returned and all is good.

Next, you need to create a custom Attribute derived from Microsoft.Practices.Unity.InterceptionExtension.HandlerAttribute. We return an instance of our previously created handler.

[AttributeUsage(AttributeTargets.Method)]
public class WcfCleanupAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
    {
        return new WcfCleanupHandler();
    }
}

In order to call our WCF client, I created a MathServiceProxy class which is simply a ‘passthrough’. Since it’s not going to do anything fancy, this class implements IProxy and IMathService (my proxy-generated WCF Service Contact). You will see why I did this later on when I hook everything up.

public class MathServiceProxy : IMathService, IProxy
{
    private MathServiceClient _client;

    [WcfCleanupAttribute]
    public int Add(int first, int second)
    {
        return _client.Add(first, second);
    }

    [WcfCleanupAttribute]
    public int Subtract(int first, int second)
    {
        return _client.Subtract(first, second);
    }

    [WcfCleanupAttribute]
    public int Multiply(int first, int second)
    {
        return _client.Multiply(first, second);
    }

    [WcfCleanupAttribute]
    public int Divide(int numerator, int denominator)
    {
        return _client.Divide(numerator, denominator);
    }

    #region IDisposable Members

    public void Dispose()
    {
        _client.Close();
    }

    #endregion

    #region IProxy Members

    public void Open()
    {
        _client = new MathServiceClient();
    }

    public void Close()
    {
        _client.Close();
    }

    public void Abort()
    {
        _client.Abort();
    }

    #endregion
}

I decorated the methods I want intercepted with my WcfCleanupAttribute. To hook all these up, I created a ProxyResolver static class with a generic method to do just that.

public static class ProxyResolver
{
    public static IServiceContract ResolveWcfProxy<iservicecontract , ProxyClass>()
        where ProxyClass : IProxy, IServiceContract, new()
    {
        IUnityContainer container = new UnityContainer();
        container.AddNewExtension<interception>();
        container.RegisterType(typeof(IServiceContract), typeof(ProxyClass));

        container.Configure</interception><interception>().SetDefaultInterceptorFor(typeof(IServiceContract), new TransparentProxyInterceptor());
        return (IServiceContract)container.Resolve(typeof(IServiceContract));
    }
}

What’s happening here is creating an Interception Extension in the Unity Applictation Block, registering our Interface to Class types and setting them up to be intercepted. It returns the actual Interface we asked for in the method, which has been “wrapped” by the framework to allow for the Interception magic to happen.

There are two Generic Types in the method, IServiceContract and ProxyClass. The constraint ensures that the ProxyClass must inherit from IServiceContract and IProxy. This constraint is put in to enable the Interception code in WcfCleanupHandler to cast our Target object to an IProxy, and perform the Open, Abort and Close cleanup code.

The following code uses the ProxyResolver to get an “intercept-able” version of our client, and we can just call the service operations normally.

IMathService proxy = ProxyResolver.ResolveWcfProxy<imathservice , MathServiceProxy>();
int sum = proxy.Divide(1, 0);

Attempting to run the code above with result in a DivideByZero Exception, and the Interception handler does it’s job by cleaning up and propagating the Exception upwards. Doing all these might seem a bit strenuous/overkill, but if this helps eliminate writing the same crappy “cleanup” code 50 times, I think it’s worthwhile.

The sample code presented so far might seem a bit complicated (or I may have complicated it :)), but do download the code and have a look. I did not explain too much about the Unity Application Block code I used, because it’s pretty lengthy. If you’re interested, download the documentation and have a read, that’s where I started for creating this example.

Lastly, do have a think about other ways on how cross-cutting can be used to simplify and reduce repeating code for yourself. Happy coding!

Download Sample Code here.

Download Enterprise Library 4.1 here. (Oct 2008 release)

Download Unity Application Block 1.2 Documentation here.

Share this post:

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: