When using a framework like NServiceBus there is usually a need to create your own unit of work in order to avoid having to repeat code over an over again in your message handlers. The following demonstrates one approach on how to implement NServiceBus Unit Of Work, for Raven DB.
Sharing the session
The first thing we need to take care of is sharing the session between our message handler(s) and the actual unit of work implementation.
We could do this using thread static but that has some issues mentioned in a post by Andreas Öhlund in a blog post that can be found here.
Instead, beginning NServiceBus v3, it is possible to use the new support for child containers
This means that all dependencies configured as single call effectively becomes static within the context of one transport message and this is exactly what we need.
To resolve a Raven document session from the container we’ll add the following configuration (StructureMap is used but any one of the other containers except Spring and Unity would work)
var store = new DocumentStore { Url = "http://localhost:8080" }; store.Initialize(); ObjectFactory.Configure(c => { c.For<IDocumentStore>() .Singleton() .Use(store); c.For<IDocumentSession>() .Use(ctx => ctx.GetInstance<IDocumentStore>() .OpenSession()); c.For<IManageUnitsOfWork>() .Use<RavenUnitOfWork>(); });
The above code tells the container to create a new IDocumentSession using the lambda specified and the fact that all message processing is done using a child container means that all message handlers processing the message will get the same session instance.
Implementing the Unit Of Work
In RavenDB you need to explicitly call IDocumentSession.SaveChanges() in order to persist your data to the database.
So to avoid making this call in all our handlers we add a unit of work implementation that takes care of it.
This saves typing and prevents us from forgetting to make the call.
public class RavenUnitOfWork : IManageUnitsOfWork { readonly IDocumentSession session; public RavenUnitOfWork(IDocumentSession session) { this.session = session; } public void Begin() { } public void End(Exception ex) { if (ex == null) session.SaveChanges(); } }
Note that we’re taking a dependency on the IDocumentSession and given that the UoW is resolved from the same child container as the handlers we’ll get the same session instance. Raven doesn’t need any special setup so the only thing we need do is call SaveChanges if End() is called and no exception occurred.
The final thing we have to do is make NServiceBus use our UoW. We do this by configuring it in the container so that NServiceBus will find it and use it:
c.For<IManageUnitsOfWork>() .Use<RavenUnitOfWork>();
What about disposing the session?
Again we’re rescued by the child containers and the fact that all single call components that were created in the child container are getting disposed by the main container when the child container is disposed.
NServiceBus is disposing the child container when the processing of a transport message is complete and this has the effect that any object implementing IDisposable will be disposed. Luckily for us IDocumentSession does just this!
With this in place it is possible to create a very clean message handlers that interact with Raven:
public class PlaceOrderHandler : IHandleMessages<PlaceOrder> { readonly IDocumentSession session; public PlaceOrderHandler(IDocumentSession session) { this.session = session; } public void Handle(PlaceOrder message) { session.Store(new Order { OrderId = message.OrderId }); } }
Working code please…
A working sample can be found over at Andreas Öhlund's github account.
- I’m using the excellent NuGetPowerTools so the solution will automatically download the required dependencies.
- Make sure to start the sample as Administrator in order to let NServiceBus create the queues for you!
- The sample assumes that you have a RavenDB at http://localhost:8080
