Extending NServiceBus: Per Unit of Work IoC Container

NServiceBus is great. That's really all there is to it. The framework is incredibly flexible such that it can be extended without too much effort. But there are a few small points of friction that we've encountered. This article shows how we've been able to address one such point.

Choose Your IoC Container

Because NServiceBus allows us to choose an IoC container rather than forcing us to use a predefined one, we gain a lot of flexibility. At the same time NServiceBus must play to the lowest-common denominator of IoC containers and thus has not been able to take advantage of all of the developments across the dependency injection world.

While most containers support "inner" or "nested" containers, some do not. Typically these inner containers are used to facilitate a unit of work, such that all resources resolved during the unit of work are shared. We happen to be using a container that does support nested scoping. Unfortunately NServiceBus does not leverage this extremely useful capability.

Furthermore, more and more containers are handling the disposal of resources explicitly upon disposal of the inner or nested container. While some containers require you to call IoC.Release(someResourceHere), others perform that work for you when you call Dispose() on the nested container.

Both of these capabilities combined—nested containers along with deterministic disposal of resources created by the nested container alleviate much of the development burden related to tracking and cleanup of resources.

In NServiceBus the logical unit of work is a message handler while the physical unit of work is the receipt of a "TransportMessage" which may contain one or more logical messages (business or application messages). This means that the physical handling or processing of a TransportMessage is the perfect place to leverage our nested container unit of work pattern.

This is where things get difficult. NServiceBus exposes no direct facility or capability to create a nested container upon receipt of a TransportMessage. While it does support the concept of message modules through implementing the IMessageModule interface, modules are not well suited to this task because they are singleton scoped and contain global, rather that unit of work-specific behavior.

A Typical Example

The best example of this problem is demonstrated with NHibernate's ISession. ISession is our unit of work for all database-related activity. If per chance we have multiple message handlers handling the same message during the same physical/TransportMessage receive we want all of them to share the same ISession. We want a single database connection rather than multiple for handling a message. Conversely, we do not want to share the session with other handlers which may be concurrently processing separate physical messages on separate threads. The current workaround is to inject ISessionFactory into your handlers directly and then call GetCurrentSession() to get the session for the thread. While this does work, it is less than ideal and represents a point of friction.

The same goes for all dependencies. While ISession is really easy because ISessionFactory exposes thread-bound instances, what about other dependencies that do not have similar capabilities? A few IoC containers offer thread-static caching, but if we're using one without that capability baked into the container, we've got to write our own.

The Solution

What if all handlers that processed a single message could share dependencies? What if all of those dependencies were deterministically and reliably disposed at the end of processing for each physical message (TransportMessage)? What if we could leverage our container's ability to create nested or inner containers?

I created a proof of concept around these very principles and I have done some exploratory testing to show that it works. Rather than modifying the NSB source code to create an extension point, I simply leverage the facilities of our IoC container. The code currently works against Autofac 1.4, but could be ported to any popular container that supports nested containers, e.g. StructureMap, Castle, Ninject, etc.

To utilize the sample, you must first compile the code in VS2008 and then add the associated assembly, NServiceBus.MessageSinks.AutofacConfiguration, to your project. At that point you change your NSB startup configuration to say:

var builder = new ContainerBuilder();

builder.Register(new MessageSinkConfigurationModule());

Conclusion

Ideally there will be extension points added to NServiceBus which will allow us to utilize nested containers during the message handling portion, but in the meantime we're able to leverage additional IoC container capabilities without touching an NServiceBus code.

I will be posting more about this and related solutions all touching on NServiceBus over the next few days.