Sunday, May 18, 2008

Integrating Castle Windsor and NHibernate with WCF

Up until now, we were using the NHibernate facility of Castle Windsor for managing our NHibernate sessions in WCF. But, we want to have a session-per-request approach as one would use when integrating NHibernate with a regular web application.

Yesterday I did a small spike to figure out how this should work. It turned out to be pretty easy. I used this excellent blog post written by Oran Dennison as my guide.

First I created a class that implemented the IServiceBehavior interface.

public class DIServiceBehavior : IServiceBehavior { private readonly ISessionFactory _sessionFactory; public DIServiceBehavior() { _sessionFactory = new Configuration() .Configure() .BuildSessionFactory(); XmlConfigurator.Configure(); // Log4Net } public void ApplyDispatchBehavior( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach(ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers) { ChannelDispatcher cd = cdb as ChannelDispatcher; if(cd != null) { foreach(EndpointDispatcher ed in cd.Endpoints) { ed.DispatchRuntime.InstanceProvider = new DIInstanceProvider (serviceDescription.ServiceType, _sessionFactory); } } } } public void AddBindingParameters( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } }

The SessionFactory is created in the constructor because the ApplyDispatchBehaviour method can be called multiple times (for each endpoint).

The next step is to create an instance provider by creating a class that implements the IInstanceProvider:

public class DIInstanceProvider : IInstanceProvider { private const String NHibernateSessionKey = "NHibernate.Session"; private readonly Type _serviceType; private IDependencyContainer _dependencyContainer; private readonly ISessionFactory _sessionFactory; private ISession _session; public DIInstanceProvider(Type serviceType, ISessionFactory sessionFactory) { Debug.Assert(null != serviceType, "null != serviceType"); _serviceType = serviceType; Debug.Assert(null != sessionFactory, "null = sessionFactory"); _sessionFactory = sessionFactory; } public Object GetInstance(InstanceContext instanceContext) { return GetInstance(instanceContext, null); } public Object GetInstance(InstanceContext instanceContext, Message message) { _dependencyContainer = new DependencyContainer(); _session = _sessionFactory.OpenSession(); _dependencyContainer .AddComponentInstance(NHibernateSessionKey, _session); return _dependencyContainer.Resolve(_serviceType); } public void ReleaseInstance(InstanceContext instanceContext, Object instance) { if(null != _session) { _session.Close(); _session = null; } if(null != _dependencyContainer) { _dependencyContainer.Dispose(); _dependencyContainer = null; } } }

I created a wrapper class around for Castle Windsor named DependencyContainer.  The GetInstance method opens a new session using the SessionFactory object we instantiated in the service behavior class. The session object is then registered with Castle Windsor. We can now implement our repositories like this:

public class SomeAggregateRepository { private ISession _session; public SomeAggregateRepository(ISession session) { _session = session; } public SomeAggregate Get(Int64 id) { return _session.Get<SomeAggregate>(id); } }

When our service operation has been executed, the NHibernate is released by the ReleaseInstance method. Finishing our example, we need to implement a custom ServiceHost and a ServiceHostFactory:

public class DIServiceHost : ServiceHost { public DependencyInjectionServiceHost() : base() { } public DIServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) { } protected override void OnOpening() { Description.Behaviors.Add(new DIServiceBehavior()); base.OnOpening(); } } public class DIServiceHostFactory : ServiceHostFactory { protected override ServiceHost CreateServiceHost( Type serviceType, Uri[] baseAddresses) { return new DIServiceHost(serviceType, baseAddresses); } }

The custom ServiceHostFactory class can now be used in the ServiceHost file required when doing IIS hosting:

<%@ ServiceHost Language="C#" Debug="true" Service="WindsorService.MyWindsorService" Factory="WindsorService.DIServiceHostFactory" CodeBehind="MyWindsorService.svc.cs" %>

There is also another way for achieving this that I will be investigating the next week or so using the WCF Integration Facility for Castle Windsor. For some reason it is not available in the current release of the Castle stack so I have to grab it from the trunk.

If you have any thoughts, improvements or suggestions I'm glad to hear them from you, my dear reader. It's the only way I'll ever learn :-).

Till next time.

No comments: