Monday, February 15, 2010

WCF and AOP with Castle.DynamicProxy

Managing logging and exception handling in WCF applications can be very time-consuming. Cross-cutting concerns can become a great burden and generate a lot of work for us. Today, I played a little bit with AOP and Castle Dynamic Proxy (DP) library. If you’re not familiar with AOP please take a look at this.

Before I even started with DP, I also considered PostSharp, but eventually I decided to go with DP. Reason: I just don’t like things integrated into VS and doing stuff I cannot fully control. It can be that this is not a legitimate reason, but still:).

I started by adding references to Castle.Core.dll and Castle.DynamicProxy2.dll files. Then I defined my Logging Aspect class

public class LoggingAspect : IInterceptor
{
    #region IInterceptor Members

    public void Intercept(IInvocation invocation)
    {
        Debug.WriteLine(string.Format("The user {0} is calling method {1}.", OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name, invocation.Method.Name));
        Debug.WriteLine("Parameters are : ");
        foreach (object argument in invocation.Arguments)
        {
            Debug.WriteLine("Parameter value : " + Convert.ToString(argument));
        }
        invocation.Proceed();
        Debug.WriteLine(string.Format("The calling is complete!"));
    }

    #endregion
}

This code is pretty straightforward. By implementing IInterceptor interface, I print out username of the calling user, the method called, and parameter values. You’ll probably want to write these info into a database or a file in your project. However, this will stands good as an example. Then, to create a proxy for a type:

ProxyGenerator pg = new ProxyGenerator();
var loggingAspect = new LoggingAspect();
var proxy = pg.CreateClassProxy(typeof(MyType), loggingAspect);

That is all I need to do regarding DP itself. Now, I need to integrate this into WCF. Luckily, WCF is very extensible framework. First, I’ll implement IInstanceProvider interface, so that I can control object instances creation. The interface is rather simple, consisting of three methods.

public class MyInstanceProvider : IInstanceProvider
{
    public Type Type { get; private set; }
    public MyInstanceProvider(Type type)
    {
        this.Type = type;
    }

    #region IInstanceProvider Members

    public object GetInstance(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
    {
        ProxyGenerator pg = new ProxyGenerator();
        var loggingAspect = new LoggingAspect();
        var proxy = pg.CreateClassProxy(this.Type, loggingAspect);
        return proxy;
    }

    public object GetInstance(System.ServiceModel.InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }
    public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, object instance)
    {
        if (instance is IDisposable)
            ((IDisposable)instance).Dispose();
    }

    #endregion
}

Note that I’m using CreateClassProxy method of ProxyGenerator to create proxies. This requires that methods we want to intercept need to be marked as virtual. Now that I have my IInstanceProvider implemented, I need to hook it up to a service. There are several options for this, but I like using attributes for this. I want to be able to mark my service implementation with some attribute so that all methods on that are intercepted in the same way. To do this I’ll need an attribute that implements IServiceBehavior interface.

public class MyAspectsAttribute : Attribute, IServiceBehavior
{

    #region IServiceBehavior Members

    public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
        System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        foreach (var dispatcher in serviceHostBase.ChannelDispatchers)
        {
            var cd = dispatcher as ChannelDispatcher;

            foreach (var endpoint in cd.Endpoints)
            {
                endpoint.DispatchRuntime.InstanceProvider = new MyInstanceProvider(serviceDescription.ServiceType);
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
    {

    }
    #endregion
}

The most important thing here is the ApplyDispatchBehavior method where I set up the instance provider for each endpoint. Next, the only thing left is to mark my service with MyAspects attribute.

[MyAspects]
public class MyGreatService : IMyGreatService 
{
    public virtual string SayHello()
    {
        return "Hello";
    }
}

This way, SayHello() method (and all others marked as virtual) will be intercepted by my LoggingAspect class thus enabling me to centralize my logging code.

No comments:

Post a Comment