WCF – NHibernate Unit Of Work Endpoint Behavior

1

OK, my last WCF-related code snippet of the day, I promise. This is quite similar to the last one. I required that the WCF service I was developing started a new NHibernate Session for each invocation, and closed it after invocation (i.e. session-per-request). I couldn’t rely on the ASP.NET session start and end events in global.asax as this particular WCF service was to respond to MSMQ messages rather than HTTP.

So, once again I found myself bashing out a custom EndpointBehavior to add a custom CallContextInitializer to every operation:

using System;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

/// <summary>
/// Being a custom EndpointBehavior added to the WCF services such that each operation begins a
/// new unit of work when invoked, and closes the same unit of work after invocation.
/// </summary>
/// <remarks>
/// In practice, we are using this simply to manage NHibernate sessions - opening a new Session at
/// the start of each operation, and closing it as the operation completes.
/// </remarks>
public class UnitOfWorkEndpointBehavior : IEndpointBehavior
{
    /// <summary>
    /// Local instance of unit of work implementation.
    /// </summary>
    /// <remarks>
    /// Interface is ORM-agnostic, it need not be NHibernate.
    /// </remarks>
    private IUnitOfWork unitOfWork;

    /// <summary>
    /// Initializes a new instance of the UnitOfWorkEndpointBehavior class.
    /// </summary>
    /// <param name="unitOfWork">Unit of Work</param>
    public UnitOfWorkEndpointBehavior(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
        // Add a new Unit of Work call context initializer to every operation.
        foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations)
        {
            operation.CallContextInitializers.Add(new UnitOfWorkCallContextInitializer(this.unitOfWork));
        }
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;

/// <summary>
/// Custom call context initializer added to all WCF operations such that each operation begins
/// a new unit of work when invoked, and closes the same unit of work after invocation.
/// </summary>
/// <remarks>
/// In practice, we are using this simply to manage NHibernate sessions - opening a new Session at
/// the start of each operation, and closing it as the operation completes.
/// </remarks>
public class UnitOfWorkCallContextInitializer : ICallContextInitializer
{
    /// <summary>
    /// Local instance of unit of work implementation.
    /// </summary>
    /// <remarks>
    /// Interface is ORM-agnostic, it need not be NHibernate.
    /// </remarks>
    private IUnitOfWork unitOfWork;

    /// <summary>
    /// Initializes a new instance of the UnitOfWorkCallContextInitializer class.
    /// </summary>
    /// <param name="unitOfWork">Unit of Work</param>
    public UnitOfWorkCallContextInitializer(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    /// <summary>
    /// Method fired after the operation is invoked.
    /// </summary>
    /// <param name="correlationState"></param>
    public void AfterInvoke(object correlationState)
    {
        // End the unit of work (closes the NH session).
        this.unitOfWork.End();
    }

    /// <summary>
    /// Method fired before the operation is invoked.
    /// </summary>
    /// <param name="instanceContext"></param>
    /// <param name="channel"></param>
    /// <param name="message"></param>
    /// <returns></returns>
    public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
    {
        // Begin a new unit of work (opens the NH session).
        return this.unitOfWork.Start();
    }
}

And the Windsor configuration is much the same as before:

container.Register(
    Component.For<UnitOfWorkEndpointBehavior>()
        .Attribute("scope").Eq(WcfExtensionScope.Services));
  1. Paul Mortimer
    Paul MortimerFriday, 9 April, 2010

    Nice .. Cherers for that Ian , I’m just chewing over a similar structure .. Who knows ,I may even post my take .

Leave a Reply