Monday, July 14, 2008

Integrating Castle Windsor and NHibernate with WCF - Throwing the WCF facility and some Rhino Tools in the mix

A while ago, I wrote this post about how to integrate Castle Windsor and NHibernate with WCF. Last weekend, I used the WCF integration facility of Castle Windsor to accomplish pretty much the same thing, but with less code. We all want that, now don't we?

As a first step, I created a class that implements the ICallContextInitializer.

public class UnitOfWorkContext : ICallContextInitializer { private IUnitOfWork _unitOfWork; public Object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message) { _unitOfWork = UnitOfWork.Start(); return null; } public void AfterInvoke(Object correlationState) { if(_unitOfWork != null) { _unitOfWork.Dispose(); _unitOfWork = null; } } }

Notice that I'm not using the SessionFactory and Session classes of NHibernate directly. Instead, I'm using the UnitOfWork classes of Ayende's most excellent Rhino Tools library.

Next an implementation of the IServiceBehavior interface needs to be created in order to apply the UnitOfWork context to the service operations.

public class UnitOfWorkBehavior : IServiceBehavior { public void ApplyDispatchBehavior( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach(var channelDispatcher in serviceHostBase.ChannelDispatchers) { var channelDispatcher = cdb as ChannelDispatcher; if(null != channelDispatcher) { foreach(var endpointDispatcher in channelDispatcher.Endpoints) { foreach(var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations) { dispatchOperation.CallContextInitializers .Add(new UnitOfWorkContext()); } } } } } public void AddBindingParameters( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } }

So far, nothing really new actually. In my previous post, I needed to create a custom ServiceHost and ServiceHostFactory class. This is now taken care of by the WCF integration facility of Castle Windsor. I'm using IIS hosting, so I need to add the DefaultServiceHostFactory of the facility to the ServiceHost file:

<%@ ServiceHost Language="C#" Debug="true" Service="WindsorService.MyWindsorService" Factory="Castle.Facilities.WcfIntegration. DefaultServiceHostFactory, Castle.Facilities.WcfIntegration" CodeBehind="MyWindsorService.svc.cs" %>

Next, we need to add the facility and the service behavior to the Windsor configuration file:

<facilities> <facility id="wcf" type="Castle.Facilities.WcfIntegration.WcfFacility, Castle.Facilities.WcfIntegration" /> </facilities> <components> ... <!-- Service behavior --> <component id="UnitOfWorkBehavior" type="WindsorService.Wcf.UnitOfWorkBehavior, WindsorService"/> ... </components>

We're almost done. As I also mentioned in my previous post, I have this wrapper class for Castle Windsor named DependencyContainer. The last thing we need to do is register the Windsor container for the WCF integration facility:

public DependencyContainer() { _windsorContainer = new WindsorContainer( new XmlInterpreter()); // For Windsor WCF facility DefaultServiceHostFactory .RegisterContainer(_windsorContainer.Kernel); // For Rhino Commons IoC.Initialize(_windsorContainer); }

An instance of the DependencyContainer is now created in the Global.ApplicationStart method:

public class Global : HttpApplication { private IDependencyContainer _dependencyContainer; protected void Application_Start(object sender, EventArgs e) { _dependencyContainer = new DependencyContainer(); } protected void Application_End(object sender, EventArgs e) { _dependencyContainer.Dispose(); _dependencyContainer = null; } }

Voila, no rocket science there either. We're done. What are we exactly gaining here? The biggest benefit is that we can now add WCF behaviors for other concerns, like exception handling, logging, transactions, etc. by just adding them to the Windsor configuration file.

Now, to round off this post I want to show the usage of the Repository library that can also be found in the Rhino Tools Library. The following needs to be added to the Windsor configuration file:

<!-- Rhino commons --> <component id="nhibernate.repository" service ="Rhino.Commons.IRepository`1, Rhino.Commons.NHibernate" type="Rhino.Commons.NHRepository`1, Rhino.Commons.NHibernate"/> <component id="nhibernate.unit-of-work.factory" service ="Rhino.Commons.IUnitOfWorkFactory, Rhino.Commons.NHibernate" type="Rhino.Commons.NHibernateUnitOfWorkFactory, Rhino.Commons.NHibernate"> <parameters> <configurationFileName>web.config</configurationFileName> </parameters> </component>

Now we can use the Repository class like so:

public IEnumerable<Customer> GetAll() { return Repository<Customer>.FindAll( DetachedCriteria.For(typeof(Customer)) .SetFetchMode("Orders", FetchMode.Eager)); } public Customer GetFor(String code) { return Repository<Customer>.Get(code); }

These repositories even have support for fetching strategies, which is actually pretty neat.

I'm happy to hear some feedback on this.

Till next time,

9 comments:

Travis V Barajas said...

Nice Article. I am just getting into Nhibernate and wondering where it made lead me. My current goal is to use Nhibernate within a middle tier. The middle tier will be WCF and I am wondering what issues I may run into. My concern is that I will not get the performance I need. Can you recommend any articles or approaches that may assist me? I want to be sure that I am handling the session manager and transactions correctly. I will not be using session per requests, so I can't use many of the examples that are out there.

Jan Van Ryswyck said...

If you're starting out with NHibernate, then the book NHibernate in Action (http://www.manning.com/kuate/) is a must. Also chackout the Summer of NHibernate (http://www.summerofnhibernate.com/).

It usually depends on the kind of service (and it's performance characteristics) you're developing in order to determine whether NHibernate is the rights choice. From my personal experience so far, NHibernate doesn't seem to be a performance bottleneck.

If you have any questions regarding NHibernate, you can also post them on the NHibernate user group (http://groups.google.be/group/nhusers?hl=nl).

tims said...

I have been using this code, and it works great. Thanks! However, I recently did some load testing and found that UnitOfWorkContext breaks down under load. _unitOfWork.Dispose();
throws a ScopeMachineryException. I changed to the following, and it seems to work fine now:

public class UnitOfWorkContext : ICallContextInitializer
{
public Object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
{
UnitOfWork.Start();
return null;
}

public void AfterInvoke(Object correlationState)
{
if (UnitOfWork.Current != null)
{
UnitOfWork.Current.Dispose();
}
}
}

Jan Van Ryswyck said...

Thanks for mentioning this. I ran into this issue myself last week. I'm going to write a follow-up post on this as soon as possible.

tims said...

I look forward to your blog post. Do you think the code I have posted will work correctly? I have used it under heavy load testing with no problems yet, but I have a nagging feeling I'm leaving some mess behind.

Jan Van Ryswyck said...

I've got something similar set up right now. I think you'll be fine :-).

Craig G said...

Good stuff.

One question though - how do you deal with commiting or rollback of the UnitOfWork? I was expecting code like this in the AfterInvoke method:

if (UnitOfWork.Current != null)
{
if (noExceptionsThrown())
UnitOfWork.Current.Comit();
else
UnitOfWork.Current.Rollback();
UnitOfWork.Current.Dispose();
}

friedm said...

Hi,

Very useful article! I am using the setup you described, where my WCF service does some data operations (read/write) when called.
The first time I call the service, it takes about 20 seconds. But every subsequent call takes less then a second!
So there seems to be some kind of startup cost, the first the you call the service.

Do you have any idea why this is, and how it might be fixed (I was thinking something in the lines of adding a little extra to the application_start? The problem is that I have no idea what that little extra has to be :) )

Thanks.

Jan Van Ryswyck said...

@CraigG: As the UnitOfWork is managed by the DI container, whenever an exception is thrown, the DI container cleans up after itself, therefore disposing the UnitOfWork and its internal session.

@friedm: Best that you use some kind of profiler (Jetbrains or RedGate both have excellent products) in order to exactly determine what's causing this delay. If you want a wild guess, then I would probably bet on creating the SessionFactory. If that's the case, then watch out for NH 2.1. There are a lot of performance improvements regarding the SessionFactory. Maybe this version of NH will solve that particular problem.