NHibernate SessionPerRequest with WcfOperationSessionContext

March 30, 2011 | NHibernate | WCF

Update: You may download a working example using NHibernate, WCF and SQLite in-memory. The example is described also here.

NHibernate 3 comes with out of the box support for the scenario where we want to have a single Session for the lifetime of a WCF request.

Using the Loquacious configuration we can easily set the WcfOperationSessionContext to be the default current session context.

var cfg = NHibernate.Cfg.Configuration();
cfg.DataBaseIntegration(ForMySQL5)
   .CurrentSessionContext<WcfOperationSessionContext>()
   .Proxy(p => p.ProxyFactoryFactory<ProxyFactoryFactory>())
   .SessionFactory()
        .GenerateStatistics();

We also need to inspect inbound and outbound application messages prior to dispatching a request message to an operation (for binding the Session) and before returning a reply (for unbinding the Session).

Here is a specific IDispatchMessageInspector implementation for that:

public sealed class NHibernateWcfContextInitializer
      : IDispatchMessageInspector
  {
      private static readonly ISessionFactory sessionFactory
          = Container.Resolve<ISessionFactory>();

      public object AfterReceiveRequest(
          ref Message request,
          IClientChannel channel,
          InstanceContext instanceContext)
      {
          CurrentSessionContext.Bind(
              sessionFactory.OpenSession());
          return null;
      }

      public void BeforeSendReply(
          ref Message reply,
          object correlationState)
      {
          var session = CurrentSessionContext.Unbind(
               sessionFactory);

          session.Dispose();
      }
  }

Next, we need to define a custom Service Behavior that applies to every single endpoint of a service. To do that we need to define a class that implements the IServiceBehavior interface.

[AttributeUsage(AttributeTargets.Class)]
public sealed class NHibernateWcfContextAttribute
    : Attribute, IServiceBehavior
{
    public void AddBindingParameters(
        ServiceDescription serviceDescription, 
        ServiceHostBase serviceHostBase, 
        Collection<ServiceEndpoint> endpoints, 
        BindingParameterCollection bindingParameters) { }

    public void ApplyDispatchBehavior(
        ServiceDescription serviceDescription, 
        ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher channelDispatcher 
            in serviceHostBase.ChannelDispatchers)
        {
            foreach (var endpoint in channelDispatcher.Endpoints)
            {
                endpoint.DispatchRuntime.MessageInspectors.Add(
                    new NHibernateWcfContextInitializer());
            }
        }
    }

    public void Validate(
        ServiceDescription serviceDescription, 
        ServiceHostBase serviceHostBase) { }
}

We can now decorate any WCF service implementation with [NHibernateWcfContext] attribute and we can access the Session using the ISessionFactory's GetCurrentSession method.

When using the agatha-rrsl project instead of providing a WcfRequestProcessor define a custom RequestProcessor (deriving from the WcfRequestProcessor) and decorating it with the [NHibernateWcfContext] attribute.

[NHibernateWcfContext]
public sealed class NHibernateWcfRequestProcessor
    : Agatha.ServiceLayer.WCF.WcfRequestProcessor { }

I would generally recommend to try all of the NHibernate's CurrentSessionContext-derived class in order to use the one that suits better for the given context. In some projects I use the WcfOperationSessionContext for the service layer and the ThreadLocalSessionContext when running Job Schedulers.