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,
Hi,
I ran into some problems using an ICallContextInitializer to manage unit of work. I discussed it here:
http://erichauser.net/?p=57
Thought it might be of interest.
Thanks for pointing this out.