Unity Interception with WCF

When working with a customer, I found a scenario where I thought it'd be "nice" to be able to inject code around WCF service method calls in a totally transparent way that'd avoid having to add any imperative code to the service.  The Unity from Enterprise Library (https://msdn.microsoft.com/en-us/library/ff663144.aspx) came to mind.  Now, while I know there are other methods of injecting behavior around WCF calls, I wanted to see if I could use Unity's inteface interception capabilities to add functionality around method calls.  The particular scenario at hand was that I wanted to create a custom performance counters for each service method and update the performance counter with the execution time of the method upon completion.  While I'm not a jedi master of either deep WCF scenarios nor of advanced performance counter application, I cobbled the following sample code together that makes it work.

The sample also illustrates how to hook into WCF internals, but that stuff is well documented elsewhere so I'll stay focused on the stuff that I found to be novel as I walk you through this SAMPLE code.

Sample Service

Nothing special.  Here's the interface definition.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace Microsoft.Samples.DPE.WcfPerformanceCounters.Web
{
 // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ISampleService" in both code and config file together.
 [ServiceContract]
 public interface ISampleService
 {
  [OperationContract]
  void DoWork();

  [OperationContract]
  void Cleanup();
 }
}

Here's where things start to get interesting.  The service implementation appears below.  I'll explain it below the code itself:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

using SampleFramework;
using SampleFramework.Diagnostics;

namespace Microsoft.Samples.DPE.WcfPerformanceCounters.Web
{
 [InterceptionBehavior(typeof(TrackMethodPerformanceBehavior))]
 [UnityInstanceProvider(typeof(ISampleService), true)]
 public class SampleService
  : ISampleService
 {
  #region Constructors

  public SampleService()
  {
  }

  #endregion

  #region Methods

  public void DoWork()
  {
   Random random = new Random();
   var secondsToWait = random.Next(2000, 10000);
   System.Threading.Thread.Sleep(secondsToWait);
  }

  public void Cleanup()
  {
   Random random = new Random();
   var secondsToWait = random.Next(1000, 50000);
   System.Threading.Thread.Sleep(secondsToWait);
  }

  #endregion
 }
}

First, note that the method implementations just sleep for a random number of seconds to simulate variable execution times and to make our performance counter data interesting.  Next, let's focus on the line "[UnityInstanceProvider(typeof(ISampleService), true)]".  This line is our primary hook into WCF internals.  It's definition appears below.  The purpose of this attribute is to allow us to replace the IInstanceProvider (https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iinstanceprovider.aspx) in the WCF pipeline.  The instance provider is the thing that actually goes about creating instances of the SampleService class when it's requested.  It's within our custom instance provider that we'll place the code that allows us to intercept calls to the service methods.  Again, the attribute definition is here:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.Diagnostics.Contracts;

using Yeast.Reflection;

namespace SampleFramework
{
 [AttributeUsage(AttributeTargets.Class)]
 public class UnityInstanceProviderAttribute
  : Attribute, IContractBehaviorAttribute, IContractBehavior
 {
  #region Constructors

  private UnityInstanceProviderAttribute()
  {
  }

  public UnityInstanceProviderAttribute(Type targetContract, bool yieldsSingleton)
  {
   Contract.Assume(targetContract != null);

   TargetContract = targetContract;
   YieldsSingleton = yieldsSingleton;
  }

  #endregion

  #region Properties

  public Type TargetContract
  {
   get;
   private set;
  }

  public bool YieldsSingleton
  {
   get;
   private set;
  }

  #endregion

  #region Methods

  public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
   {
   var providerGenericType = typeof(UnityInstanceProvider<,>);
   var providerConstructedType = providerGenericType.MakeGenericType(TargetContract, dispatchRuntime.Type);
   var provider = TypeFactory.CreateInstance(providerConstructedType) as IUnityInstanceProvider;
   provider.DispatchRuntime = dispatchRuntime;
   provider.YieldsSingleton = YieldsSingleton;
   dispatchRuntime.InstanceProvider = provider;
   }

  public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
  {
  }

  public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  {
  }

  public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
  {
  }

  #endregion
 }
}

The important point here is that we need access to the DispatchRuntime so that we can set the InstanceProvider property on it.  I've done some monkey business with generics here so that I could take advantage of .NET type system so that I could have one IInstanceProvider type dynamically created for each service type I'm manipulating.  This also allows me to use the new() operator when creating service instances as opposed to having to use Activator.  Below you'll find the source code for the instance provider.  This is where the magic happens and I invoke unity interception APIs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.Diagnostics.Contracts;

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.StaticFactory;
using Microsoft.Practices.Unity.Utility;
using Microsoft.Practices.Unity.InterceptionExtension;

using Yeast;
using Yeast.Reflection;

namespace SampleFramework
{
 public class UnityInstanceProvider<ContractType, ServiceType>
  : IUnityInstanceProvider
  where ServiceType : class, ContractType, new()
  where ContractType : class
 {
  #region Fields

  private static object _singletonLockTarget = new object();

  #endregion

  #region Constructors

  public UnityInstanceProvider()
  {
  }

  #endregion

  #region Properties

  public static ContractType SingletonService
  {
   get;
   private set;
  }

  public DispatchRuntime DispatchRuntime
  {
   get;
   set;
  }

  public bool YieldsSingleton
  {
   get;
   set;
  }

  #endregion

  #region Methods

  public ContractType InstantiateService()
   {
   var behaviors =
     (from attribute in typeof(ServiceType).GetAttributes<InterceptionBehaviorAttribute>(false)
     let behavior = TypeFactory.CreateInstance(attribute.BehaviorType)
     select behavior as IInterceptionBehavior).ToList();
   
   var service = new ServiceType();
   var proxiedService = Intercept.ThroughProxy<ContractType>(
    service
     , new InterfaceInterceptor()
     , behaviors);
   return proxiedService;
   }

  public object GetInstance(InstanceContext instanceContext, Message message)
  {
   object instance;

   if (YieldsSingleton)
   {
    lock (_singletonLockTarget)
    {
     if (SingletonService == null)
     {
      SingletonService = InstantiateService();
     }

     instance = SingletonService;
    }
   }
   else
   {
    instance = InstantiateService();
   }

   return instance;
  }

  public object GetInstance(InstanceContext instanceContext)
  {
   return GetInstance(instanceContext, null);
  }

  public void ReleaseInstance(InstanceContext instanceContext, object instance)
  {
   if (instance is IDisposable)
   {
    (instance as IDisposable).Dispose();
   }
  }

  #endregion
 }
}

The most important method is the InstantiateService method that's called by the GetInstance method.  It serves two key purposes:

  • Looks for all InterceptionBehaviorAttribute instances that have been applied to the ServiceType.  This extensibility point is reading the metadata applied to the service class via a lined of code that I have yet to explain.  That is the [InterceptionBehavior(typeof(TrackMethodPerformanceBehavior))] attribute.  This is another of my custom attributes and is not WCF related per se.  It's basically hooking up the TrackMethodPerformanceBehavior class, which does the work of creating and using the performance counters that this whole exercise was really all about.
  • Creates an instance of the service class and calls the Unity method Intercept.ThroughProxy.  The proxied service instance is returned instead of the raw service instance.  This is "the main thing".

The base interface, IUnityIntstanceProvider looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace SampleFramework
{
 public interface IUnityInstanceProvider
  : IInstanceProvider
 {
  DispatchRuntime DispatchRuntime
  {
   get;
   set;
  }

  bool YieldsSingleton
  {
   get;
   set;
  }
 }
}

This inteface is defined so that I can access the properties defined in non-generic code on instances of UnityInstanceProvider<,>.

Here's what the InterceptionBehaviorAttribute looks like.  It's dead simple:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

using Microsoft.Practices.Unity.InterceptionExtension;

namespace SampleFramework
{
 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
 public class InterceptionBehaviorAttribute
  : Attribute
 {
  #region Constructors

  private InterceptionBehaviorAttribute()
  {
  }

  public InterceptionBehaviorAttribute(Type behaviorType)
  {
   Contract.Assume(behaviorType != null);
   Contract.Assume(typeof(IInterceptionBehavior).IsAssignableFrom(behaviorType));

   BehaviorType = behaviorType;
  }

  #endregion

  #region Properties

  public Type BehaviorType
  {
   get;
   private set;
  }

  #endregion
 }

Next, I'll share the code for the TrackMethodPerformanceBehavior which implements the Unity IInterceptionBehavior interface so as to add the needed functionality before and after the method invocation:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.Diagnostics.Contracts;
using System.Threading;

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.StaticFactory;
using Microsoft.Practices.Unity.Utility;
using Microsoft.Practices.Unity.InterceptionExtension;

using Yeast;
using Yeast.Reflection;
using Yeast.Threading;
using Yeast.Collections.Generic;

namespace SampleFramework.Diagnostics
{
 public class TrackMethodPerformanceBehavior
  : IInterceptionBehavior
 {
  #region Fields

  private static ReaderWriterLockSlim _dictionaryLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

  #endregion

  #region Constructors

  public TrackMethodPerformanceBehavior()
  {
   TypeCategoryMap = new Dictionary<Type, IServiceContractPerformanceCategory>();
  }

  #endregion

  #region IInterceptionBehavior Members

  public IEnumerable<Type> GetRequiredInterfaces()
  {
   return new List<Type>();
  }

  public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
   {
   IMethodReturn returnValue;

   var beforeTime = DateTime.Now;

   returnValue = getNext().Invoke(input, getNext);

   var afterTime = DateTime.Now;
   var millisecondDelta = afterTime.Subtract(beforeTime).TotalMilliseconds;
   var category = this[input.Target.GetType()];
   var counter = category.MethodCounterMap[input.MethodBase];
   counter.RawValue = (int)millisecondDelta;

   return returnValue;
   }

  public bool WillExecute
  {
   get
   {
    return true;
   }
  }

  public static Dictionary<Type, IServiceContractPerformanceCategory> TypeCategoryMap
  {
   get;
   private set;
  }

  public IServiceContractPerformanceCategory this[Type serviceType]
  {
   get
   {
    IServiceContractPerformanceCategory category;

    using (var readerLock = new UpgradeableReaderLockContext(_dictionaryLock))
    {
     if (!TypeCategoryMap.TryGetValue(serviceType, out category))
     {
      using (var writerLock = readerLock.GetWriterContext())
      {
       if (!TypeCategoryMap.TryGetValue(serviceType, out category))
       {
        category = ServiceContractPerformanceCategoryFactory.GetForContract(serviceType);
        TypeCategoryMap.Add(serviceType, category);
       }
      }
     }
    }

    return category;
   }
  }

  #endregion
 }
}

I've highlighted the important code in bold red.  This method illustrates the raw power of unity to intercept method calls transparently so as to add code before and after method execution or even totally reimplement a method call.  Tangentially, I'm using a Yeast class called UpgradeableReaderLockContext that I'll post a blog article about later.  It provides an IDisposable-based wrapper around the ReaderWriterLockSlim class at makes for clean syntax.

Finally, I'll present the code that backs the expression ServiceContractPerformanceCategoryFactory.GetForContract(serviceType); .  It's an ultra simple method that just does some reflection to create the constructed type that does the fun work:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Yeast.Reflection;

namespace SampleFramework.Diagnostics
{
 public static class ServiceContractPerformanceCategoryFactory
 {
  public static IServiceContractPerformanceCategory GetForContract(Type contractType)
  {
   var genericCategory = typeof(ServiceContractPerformanceCategory<>);
   var constructedCategory = genericCategory.MakeGenericType(contractType);
   var category = TypeFactory.CreateInstance(constructedCategory);

   return category as IServiceContractPerformanceCategory;
  }
 }
}

The ServiceContractPerformanceCategory<> generic class is constructed using the WCF service type, instantiated, and returned.  It does the bulk of the work.  Note that, at this point in this blog article, I've already really demonstrated how to do the main point of the article which was to use Unity with WCF.  The rest of this is interesting only insofar as you care about the code I used to dynamically create and use custom performance counters to instrument service methods.  Here's the source for ServiceContractPerformanceCategory:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Reflection;
using System.Diagnostics.Contracts;
using System.Diagnostics;

using Yeast.Reflection;

namespace SampleFramework.Diagnostics
{
 public class ServiceContractPerformanceCategory<ServiceType>
  : IServiceContractPerformanceCategory
  where ServiceType : class
 {
  #region Fields

  private static Type _serviceTypeInstance;
  private static Type _contractTypeInstance;
  private static List<MethodInfo> _operations;
  private static Dictionary<MethodBase, PerformanceCounter> _methodCounterMap;
  private static PerformanceCounterCategory _category;

  #endregion

  #region Constructors & Setup

  static ServiceContractPerformanceCategory()
  {
   _serviceTypeInstance = typeof(ServiceType);

   var serviceContractAttribute = _serviceTypeInstance.GetAttribute<ServiceContractAttribute>(false);

   if (serviceContractAttribute == null)
   {
    var serviceInterface =
     (from type in _serviceTypeInstance.GetInterfaces()
      let attribute = type.GetAttribute<ServiceContractAttribute>(false)
      where attribute != null
      select new { Type = type, Attribute = attribute }).FirstOrDefault();

    if (serviceInterface == null)
    {
     throw new Exception(String.Format("Could not find an interface implemented by {0} that is decorated with {1}.", _serviceTypeInstance, typeof(ServiceContractAttribute)));
    }

    serviceContractAttribute = serviceInterface.Attribute;
    _contractTypeInstance = serviceInterface.Type;
   }
   else
   {
    _contractTypeInstance = _serviceTypeInstance;
   }

   _operations =
    (from operation in _contractTypeInstance.GetMethods(BindingFlags.Instance | BindingFlags.Public)
     let operationContractAttribute = operation.GetAttribute<OperationContractAttribute>(false)
     where operationContractAttribute != null
     select operation).ToList();

   EnsureCreated();

   _methodCounterMap = new Dictionary<MethodBase, PerformanceCounter>();
   foreach (var operation in _operations)
   {
    _methodCounterMap.Add(operation, new PerformanceCounter(PerformanceCategoryName, operation.Name, false));
   }
  }

  public ServiceContractPerformanceCategory()
   : this(true)
  {
  }

  public ServiceContractPerformanceCategory(bool shouldRecreate)
  {
   if (shouldRecreate && PerformanceCounterCategory.Exists(PerformanceCategoryName))
   {
    PerformanceCounterCategory.Delete(PerformanceCategoryName);
   }

   EnsureCreated();
  }

  private static void EnsureCreated()
  {
   try
   {
    if (!PerformanceCounterCategory.Exists(PerformanceCategoryName))
    {
     var creationData = new CounterCreationDataCollection();
     foreach (var operation in _operations)
     {
      creationData.Add(new CounterCreationData()
      {
       CounterType = PerformanceCounterType.NumberOfItems32,
       CounterName = operation.Name
      });
     }

     _category = PerformanceCounterCategory.Create(
      PerformanceCategoryName
      , null
      , PerformanceCounterCategoryType.SingleInstance
      , creationData);
    }
    else
    {
     _category = new PerformanceCounterCategory(PerformanceCategoryName);
    }
   }
   catch (Exception exception)
   {
    throw new Exception(String.Format("Failed to create performance category {0}.", PerformanceCategoryName), exception);
   }
  }

  #endregion

  #region Properties

  public static string PerformanceCategoryName
  {
   get
   {
    return _contractTypeInstance.Name;
   }
  }

  public Type ServiceTypeInstance
  {
   get
   {
    return _serviceTypeInstance;
   }
  }

  public Type ContractTypeInstance
  {
   get
   {
    return _contractTypeInstance;
   }
  }

  public List<MethodInfo> Operations
  {
   get
   {
    return _operations;
   }
  }

  public Dictionary<MethodBase, PerformanceCounter> MethodCounterMap
  {
   get
   {
    return _methodCounterMap;
   }
  }

  #endregion
 }

Problem

When running the sample application in a Windows Azure web role, I got an exception due to the fact that the service account that's used to run the application pool does not have sufficient permissions to write to the registry so as to create the performance counters.  I now need to investigate how to configure things in Azure so that it will work properly there as well.  My initial thought process is that I may need to use a startup task, but we'll see.  Please provide any ideas you have there.