WCFContext and Inversion of Control in WCF with Unity and ServiceLocator

Introduction

In previous posts here and here, I have covered configuring Unity for ASP.net MVC. I am going to build upon those posts and cover configuring Unity as an Inversion of Control (IOC) container for WCF, so that patterns such as UnitOfWork can be implemented cleanly. Along the way, I am going to propose a solution for the use a HttpContext equivalent for non-ASP.net WCF, the WCFContext.

  1. The WCF Context
  2. The WCF Context Message Inspector
  3. Unity and WCF Managed Lifetime Objects
  4. Context Lifetime Managers Revisited
  5. Putting it all together
  6. Conclusion

One of things that is very convienant about developing web applications is the availability of the HttpContext as an ambient item store. For example, When using an IOC container for ASP.net one of the options we can use is to use a HttpConext lifetime managed object. The current items for a HttpRequest can be accessed anywhere using HttpContext.Current.Items. A similar facility does not exist for WCF services however( unless runnng with ASP.net compatibility, but it’s not always possible or desirable to run in this mode).

To create the equivalent facility in wcf we need to make use of thread local storage, and make sure that items created on a thread are destroyed before the thread is reused by another request.

The WCFContext

The source code for the WCFContext is shown below. The things to note are the use of the ThreadStatic attribute for the private static variable threadinstance, this ensures that each thread gets it’s own copy of the static variable (they are not shared), and the the static getter for the current instance that creates the instance if it does not exist – there is no public constructor.

There is also a method for clearing the context, which will be become important when we process the end of a WCF message.

/// Provides an accesible Context API similar to the HttpContext but not requiring
/// the ASP.Net runtime.
public class WCFContext{      

  [ThreadStatic]
  private static WCFContext threadInstance;
 
  private IDictionary items;

  private WCFContext(){
    items = new ListDictionary();
  }
 
  public IDictionary Items{get{return items;}}
 
  public static WCFContext Current{
    get{

      if (threadInstance == null){
        threadInstance = new WCFContext();
      }
      return threadInstance;
    }

  }
 
  public void Initialise(){
    this.Items.Clear();
  }      
}

The WCFContext message inspector

To hook the WCFContext onto the WCF call, I have used a message inspector. Things to note are the possible use of an endrequest callback, and the calling of initialise() on the start of the request, and the clearing of the context at the end of the request.

public class WCFSingleCallContextMessageInspector : IDispatchMessageInspector{
  private Action onEndRequest = null;

  public WCFSingleCallContextMessageInspector(Action onEndRequest){
    this.onEndRequest = onEndRequest;
  }

  ///Just after a message is received by the service - make sure we clear the context
  ///even it's unnecesary (being defensive)
  public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,
    System.ServiceModel.IClientChannel channel,
    System.ServiceModel.InstanceContext instanceContext){
    WCFContext.Current.Initialise();
    return request;
  }

  ///The service call has ended, dispose any resources
  ///If a call back handler has been supplied, call it
  public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, 
                                                     object correlationState){
    if (this.onEndRequest != null){
      onEndRequest();
    }

    WCFContext.Current.Items.Clear();
  }
}

This message inspector can be incorporated into the WCF service channel stack in the normal ways, but I am going to move onto using this WCFContext to facilitate the use LifeTime managed objects in WCF using Unity 2.0.

Unity and WCF Lifetime managed objects

In unit of work scenarios it is important to have the concept of an amibient "session", whether that is a NHibernate ISession or a EntitytFramework DbContext. Typically you would access this session though class repositories. Now you don't want to go passing and keeping track of your session through every method of your service and business layers, so you probably want to use a IOC container to manage the lifetime of the session for you.

The trick with services, as I've already mentioned, is that there is no context out of the box. The code above addresses this.

It is also reasonble to want to implement constructor dependency in your services, so that they instantiated with any required dependencies.

I will now demonstrate a technique to enable Unity IOC for WCF services that enables the use of WCF lifetime managed objects and service dependency injection. The first item of code is the ServiceLocatorServiceInstanceProvider class. This is an implementation of System.ServiceModel.Dispatcher.IInstanceProvider, which is called by the WCF dispatcher to provide an instance of the specified service type.

Note that this is not Unity specific - it uses the common ServiceLocator API to resolve the service type

public class ServiceLocatorServiceInstanceProvider : IInstanceProvider{
  
  private readonly Type serviceType;

  public ServiceLocatorServiceInstanceProvider(Type serviceType){
    this.serviceType = serviceType;
  }

  public object GetInstance(
                System.ServiceModel.InstanceContext instanceContext,
                System.ServiceModel.Channels.Message message){
     return GetInstance(instanceContext);
  }

  public object GetInstance(System.ServiceModel.InstanceContext instanceContext){
    return ServiceLocator.Current.GetInstance(serviceType);
  }

 
  public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, 
                                                                 object instance){
      //nothing to do
  }

}

To apply the provider, I've create a ServiceLocatorServiceBehavior class as follows. Again, this is not coupled to Unity - you could implement this class using Stucturemap or Unity with the appropriate derivation. Please note

  • The InstanceProvider on each dispatcher is set to an instance of ServiceLocatorServiceInstanceProvider for the specified service.
  • The IDispatchMessageInspectors are retrived from the container to add to the dispatch runtime.
public abstract class ServiceLocatorServiceBehavior: Attribute, IServiceBehavior{
  public void AddBindingParameters(ServiceDescription serviceDescription,
                System.ServiceModel.ServiceHostBase serviceHostBase,
                System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
                System.ServiceModel.Channels.BindingParameterCollection bindingParameters){
      //nothing to do
  }

  //this is implemented by the derived class, either Unity or Structuremap 
  protected abstract void EnsureContainerConfigured();

  public void ApplyDispatchBehavior(ServiceDescription serviceDescription, 
                             System.ServiceModel.ServiceHostBase serviceHostBase){
    //make sure that the container has been configured - 
    //\should also set the common service locator
    EnsureContainerConfigured();

    foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers){
      ChannelDispatcher cd = cdb as ChannelDispatcher;
      if (cd != null){
        foreach (EndpointDispatcher endpointDispatcher in cd.Endpoints){
          endpointDispatcher.DispatchRuntime.InstanceProvider = 
                   new ServiceLocatorServiceInstanceProvider(serviceDescription.ServiceType);
 
          foreach (IDispatchMessageInspector inspector in
                 ServiceLocator.Current.GetAllInstances<IDispatchMessageInspector>()){
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
          }                                      
        }
      }
    }
  }

  public void Validate(ServiceDescription serviceDescription, 
                                        System.ServiceModel.ServiceHostBase serviceHostBase){
    //nothing to do
  }       
}

The unity version of this service behavior is thus (where I have made use of Unity bootstrapping technique).

public class UnityInstanceServiceBehavior: ServiceLocatorServiceBehavior{
  protected override void EnsureContainerConfigured(){
    UnityInstance.Initialise();
  }
}

Context LifeTime Managers Revisted

In a previous post, I proposed a design for use of HttpContextLifetimeManager for Unity. Because of the obvious similariites between a HttpContext and a WCFContext, I have improved the design to a common approach around the idea of a IContextItemsProvider, the implementation of which is just a simple wrapper around either the HttpContext.Items or WCFContext.Items collection.

public interface IContextItemsProvider{
  IDictionary Items { get; }
}

public class WCFContextItemsProvider : IContextItemsProvider{
  public IDictionary Items{
    get{return WCFContext.Current.Items; }
  }
}

public class HttpContextItemsProvider : IContextItemsProvider{
  public IDictionary Items{
    get{return HttpContext.Current.Items; }

  }
}

public class ContextLifetimeManager : LifetimeManager{      

  /// Context collection key, the value of which is a list of ContextLifetimeManager

  private static ListDictionary allInstances;

  static ContextLifetimeManager(){
    allInstances = new ListDictionary();
  }      

  /// The key of the instance being managed in the context provider
  private Guid contextKey = Guid.NewGuid();

  /// The items context provider
  private IContextItemsProvider itemProvider = null;

  public ContextLifetimeManager(IContextItemsProvider itemsProvider){
    this.itemProvider = itemsProvider;

    if (allInstances.Contains(itemsProvider) == false){
      allInstances.Add(itemsProvider, new List<ContextLifetimeManager>());
    }

    (allInstances[itemsProvider] as List<ContextLifetimeManager>).Add(this);
  }

  /// Retrieves all manages from the current HttpContext and disposes of them 
  public static void ReleaseAndDisposeAll(IContextItemsProvider itemsProvider){
    if (allInstances.Contains(itemsProvider)){
      foreach (ContextLifetimeManager manager in
                allInstances[itemsProvider] as List<ContextLifetimeManager>){
        manager.ReleaseAndDispose();
      }
    }                     
  }

  protected IDictionary Items{
    get{return itemProvider.Items;}
  }

  /// Retrieves the managed object
  public override object GetValue(){
    if (Items.Contains(contextKey) == false){
      return null;
    }
 
    return Items[contextKey];
  }

 
  /// Removes the reference to the managed object.
  /// Note that this does NOT dispose the object
  public override void RemoveValue(){
    if (Items.Contains(contextKey)){
      Items.Remove(contextKey);
    }
  }

  /// Sets the object to be managed
  public override void SetValue(object instance){
    Items[contextKey] = instance;
  }

  /// Disposes the object if it can be disposed and removes any references to it
  private void ReleaseAndDispose(){
    IDisposable disposable = GetValue() as IDisposable;
    if (disposable != null){
      disposable.Dispose();
    }
    RemoveValue();
  }
}

Putting it all together

To put it all together, we need to configure the service to use the ServiceLocatorServiceBehavior of our choice (UnityInstanceServiceBehavior), and then set up the container.

Setting up the service simply involves decorating the service with the neccesary attribute:

[UnityInstanceServiceBehavior]  
public class MyService : IMyService{

//.....service implementation....

}

I've used my bootstrapping technique to configure the UnityContainer, here's an implementation of the Configurator. I'm using the container to manage the instance of the WCFContextItemsProvider as a singleton, which is then resolved during the construction of each ContextLifetimeManager.The real important thing is the use of the end request call back, which is passed in as a constructor argument to the WCFSingleCallContextMessageInspector that ensures that the lifetime managers dispose of their managed resources.

public class ServiceUnityConfigurator : IUnityConfigurator{
  public void Configure(Microsoft.Practices.Unity.IUnityContainer container){
    
    container.RegisterInstance<IContextItemsProvider>(new WCFContextItemsProvider());
    
    Action OnEndRequest = () => { 
	ContextLifetimeManager.ReleaseAndDisposeAll(container.Resolve<IContextItemsProvider>()); 
    };

    container.RegisterType<ILogger, Logger>(container.Resolve<ContextLifetimeManager>());

    container.RegisterType<IDispatchMessageInspector, 
                 		WCFSingleCallContextMessageInspector>
					("WCFContext", new InjectionConstructor(OnEndRequest));

  }
}

If you were using a HttpContextItemsProvider in ASP.net, you would need implement the Application_EndRequest method in Global.asax as follows to get the ContextLifetimeManager to dispose of it's resources.

void Application_EndRequest(object sender, EventArgs args){
  ContextLifetimeManager.ReleaseAndDisposeAll(ServiceLocator.GetInstance< IContextItemsProvider >());
}

Conclusion

This post has covered the use of an ambient context for WCF services in the absence of ASP.net. I have shown how this context can be used by life time managers to provide container controller lifetime managed dependency injection using Unity in WCF and provided code for a common ContextItemsLifetimeManager class that can be used for both ASP.net web applications and WCF applications.

kick it on DotNetKicks.com

About these ads
This entry was posted in ASP.Net MVC, Dependency Injection/Inversion of Control, Design Patterns, Unity, WCF and tagged , , , , . Bookmark the permalink.

One Response to WCFContext and Inversion of Control in WCF with Unity and ServiceLocator

  1. NIce article! I was wondering, what if I wanted to do all of the wireup via configuration — any thoughts on how to pass the context provider to the constructor of the lifetime manager?

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