NHibernate on WCF

Note that I have posted two follow up discussions on this along with a number of code changes.  They can be found at:
WCF and nHibernate redux
A little more WCF NHibernate

I know that there are a few blog posts out there on this subject, so I feel like I'm repeating myself while writing this.  Bear with me...please.  Over the last month I've been chasing an ISession corruption error in our application with little success.  During that time I spent a lot of time looking at our WCF implementation to ensure that it wasn't some piece of that which was causing the problem.  Needless to say, it wasn't, but I have a much greater appreciation for the extensibility of WCF as a result.  Part of my research/debugging/hacking around/crying was looking into the most appropriate way to create, persist and end a nHibernate ISession object during each individual WCF OperationContract call.  Here's what I figured out and created.

The first, and easiest thing that you need to do is set the ServiceContract Attribute to have an InstanceContextMode =  InstanceContextMode.PerCall.  The key here is that I want per call isolation.  Choosing thie InstanceContextMode will force the creation of a new service context for each client request and ultimately create a new InstanceContext object for each call.  What you're doing here is telling WCF that every call to an OperationContext should be treated as a distinctly separate code execution area.  If you were to leave InstanceContextMode to its default (InstanceContextMode.PerSession) the execution area of the code would create an InstanceContext for the duration of the Channel's life. Basically, that would mean that creating a connection to the WCF service would create a new InstanceContext and all OperationContracts would have access to it until the channel was released.  This will allow you to access items in the InstanceContext across calls, which is not how we want to handle our ISessions.

[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[NHibernateContext]
public interface ICustomerServices
{
    [OperationContract]
    IEnumerable<CustomerListingDto> RetrieveListingOfAll();
}

So we've created a service that is set to have PerCall InstanceContexts.  This is the first step to creating and storing the ISession.  Putting an ISession into the InstanceContext requires us to create a custom extension using IExtension<T>.  All our extension needs to do is provide a place to store the ISession object for that OperationContract call.  The extensions on the InstanceContext have a lifetime that lasts only for the duration of the call.  In the end the extension looks like this:

public class NHibernateContextExension : IExtension<InstanceContext>
{
    public NHibernateContextExension(ISession session)
    {
        Session = session;
    }

    public ISession Session { get; private set; }

    public void Attach(InstanceContext owner)
    {}

    public void Detach(InstanceContext owner)
    {}
}

Now that we have that extension we need to get it attached to each and every OperationContract call that is handled by our service.  An IInstanceContextInitializer implementing class is used to trigger the addition of the extension to the InstanceContext.  Every time that an InstanceContext is initialized (which will be on every Operation call since we're using InstanceContextMode.PerSession) the NHibernateContextExtension is added to the newly created InstanceContext.  Also note that it is at this point that the NHibernate ISession object is created and assigned to the InstanceContext's extension.  It's here that the nHibernate session per operation call concept is implemented.

public class NHibernateContextInitializer : IInstanceContextInitializer
{
    public void Initialize(InstanceContext instanceContext, Message message)
    {
        instanceContext.Extensions.Add(
            new NHibernateContextExtension(
                NHibernateFactory.OpenSession()
                )
            );
    }
}

You probably noticed in the NHibernateContextInitializer class above that the ISession object is being created from a NHibernateFactory object.  This object is a wrapper for the native NHibernate ISessionFactory object.  We need it implemented in this way so that we have a technique for accessing a session factory that is created outside of the scope of this little framework.  Later in this post you will see how this object is used and initialized with a valid SessionFactory object.

public static class NHibernateFactory
{
    private static ISessionFactory _sessionFactory;

    public static void Initialize()
    {
        Initialize(new Configuration().Configure().BuildSessionFactory());
    }

    public static void Initialize(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public static ISession OpenSession()
    {
        return _sessionFactory.OpenSession();
    }
}

With a way to attach a new ISession to every InstanceContext that is created, we need to trigger this process on each and every call.  To do that we will attribute the WCF service with a custom attribute.  All we need to do is create a class that is an Attribute and implements the IContractBehavior interface.  During ApplyDispatchBehavior we add an NHibernateContextIntializer to start the entire process of creating the new NHibernate ISession for each call.

public class NHibernateContextAttribute : Attribute, IContractBehavior
{
    public ISessionFactory SessionFactory { private get; set; }

    public void Validate(ContractDescription contractDescription, 
                            ServiceEndpoint endpoint)
    {}

    public void ApplyDispatchBehavior(ContractDescription contractDescription, 
                            ServiceEndpoint endpoint, 
                            DispatchRuntime dispatchRuntime)
    {
        dispatchRuntime.InstanceContextInitializers.Add(
            new NHibernateContextInitializer());
    }

    public void ApplyClientBehavior(ContractDescription contractDescription, 
                            ServiceEndpoint endpoint, 
                            ClientRuntime clientRuntime)
    {}

    public void AddBindingParameters(ContractDescription contractDescription, 
                            ServiceEndpoint endpoint, 
                            BindingParameterCollection bindingParameters)
    {}

Simply creating a new ISession for each call to the WCF service isn't enough.  We need to be able to work with that ISession object in our data access endeavours. To do that I've created a simple class with a static method on it that retrieves the NHibernateContext Extension from the current InstanceContext.  This is possible because there is only one InstanceContext during the life of each service call.  To make this fall in line with other 'Context' object that we are used to working with (like HttpContext) I've created the static method called Current.

public class NHibernateContext
{
    public static NHibernateContextExension Current()
    {
        return OperationContext.Current.
                InstanceContext.Extensions.
                Find<NHibernateContextExension>();
    }
}

All of that explanation is well and good, but the real trick is how the hell do you use this.  That is fairly simple.  You need to do three things. First you need to create and initialize a nHibernate SessionFactory and inject it into the custom wrapper that is provided in this code.  Normally you would want to do this once for the life of the application as shown by placing this code in the Application_Start of the global.asax file.  This code snippet shows two options for initializing the factory. The first line will automatically load a nHibernate SessionFactory using the hibernate.cfg.xml file that is in the same folder as the IglooCoder.Commons.dll file.  The second line allows more flexibility to initialize a nHibernate SessionFactory in the way that you need or want.

protected void Application_Start(object sender, EventArgs e)
{
    NHibernateFactory.Initialize();
    //or//
    NHibernateFactory.Initialize(new Configuration().Configure().BuildSessionFactory());
}

Second you need to attribute any services with the NHibernateContextAttribute) that will need to access data using nHibernate.

[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[NHibernateContext]
public interface ICustomerServices
{
    [OperationContract]
    IEnumerable<CustomerListingDto> RetrieveListingOfAll();
}

The final step is to make use of the ISession object that the framework is creating for each call.  As you can see in the code below there is a bit of a trick to using t

public abstract class BaseRepository<T> : IRepository<T>
{
    public T FetchById(int id)
    {
        return NHibernateContext.Current().Session.Get<T>(id);
    }
}

If you want to use this code and not have to write it all out you can grab the source from the project's Subversion trunk (https://igloocoder.net:8445/svn/IglooCommons/trunk/).  Compile the code running b.bat at the command line and you will find the compiled assemblies in the \compile folder.

My next post will talk about how to maintain testability while using this stuff.

Ideas for implementing this came from Dan Rigsby's posts on WCF extension here and here.

posted @ Monday, July 21, 2008 8:16 PM

Print

Comments on this entry:

# re: NHibernate on WCF

Left by Morten Lyhr at 7/23/2008 3:45 AM
Gravatar
Great stuff!

Is there any way to get an IoC Containter to do this?

Ideally, I would like my WCF and ASPX/Monorail apges, to share the same default NHibernate ISession, with just ctor injection.

NHibernateContext.Current, should just be a ctor injection of ISession.

What about transaction management and flushing of the session?

# re: NHibernate on WCF

Left by Donald Belcham at 7/24/2008 6:43 AM
Gravatar
@Morten -- I'm currently running this pattern in combination with Castle Windsor on a project. I've not tried to make it work solely with an IoC container. The problem that will need to be solved there is how you create and initialize the NHibernate SessionFactory only one time for the life of application.
I have no plans to build support in for using the same ISession on both the client and service side of the WCF connection. Realistically, it is a bad separation of concerns to have it in both locations. Your ISession should exist only on the service side of that conversation.
Currently I have added transactioning or an explicit flush into the code. It will get there. Because the ISession is always available through NHibernateContext.Session, you can perform a Flush() and/or opening and closing of transactions at any point you want in your code.

# re: NHibernate on WCF

Left by chr at 8/5/2008 9:17 AM
Gravatar
Hi,
I might be missing the point completely, but, in InstanceContextMode.PerCall a new instance of the service is created for each operation, and it is disposed immediately afterwards. Therefore, given a factory that can provide nhibernate sessions, a reference to the current session could be held in the service itself, and the session could be disposed of in the dispose method. Wouldn’t this work?

# re: NHibernate on WCF

Left by Pete Grazaitis at 9/10/2008 9:39 PM
Gravatar
I have been reading quite a bit about this and I think I need to bounce a couple of questions to see if my thinking is flawed.

I typically offload my business logic/DAO mash-up inside of a separate business layer and my services act as a type of Facade to this class library. Because of this would storing NHibernate Sessions in the OperationContext be a symptom of mixing concerns and couple things a bit tighter?

I was considering extending Bill McAfferty's (sp?) great framework to also utilize OperationContext inside the SessionManagement but it got me thinking.

The goal of storing Sessions inside of an HttpContext/OperationContext is to provide a type of "Global" access to the unique consumer vs static for all to share/mangle, correct?

In a Session per request model, do we really need to associate Session with the OperationContext?

Futhermore, as I recall building the SessionFactory is a performance issue and their is desire to only perform this once. Is it possible to get the best of both worlds?

# re: NHibernate on WCF

Left by Donald Belcham at 9/14/2008 10:54 AM
Gravatar
@Pete You're thinking very clearly here. I think there a couple of things that I can say to (hopefully) clear up your questions.

With the Session per operation method the goal of storing in a HttpContext.CurrentContext (for asmx implementations) or OperationContext (for WCF implementations) is to provide one session for the life of the call. We want to treat our operation calls as if they are a single grouping of work that needs to be performed. As a result we should be using a single session for that work. Because we need to have the Session available for many different pieces of work which could (and probably should) occur in multiple locations with the code, we need a central location to store it. Putting an nHibernate Session into the OperationContext does appear to be a coupling. What I've done on my most recent implementation of this is create an abstraction between the OperationContext and the Session. Something (I think) I called an ISessionStorageLocation. That interface is implemented on an OperationContextSessionStorage as well as a LocalMemorySessionStorage. Because my code only cares about ISessionStorageLocation, it is not tightly coupled to the OperationContextSessionStorage implementation nor the OperationContext.

The "Global" nature of the access that you mention is global only within the scope of that operation call. It is not global across multiple calls from a single or many different clients.

Loading SessionFactory only one time for the life of your application is definitely what you want to do. I load my session factory when the application loads and then store it in a static location (like off of the Global.asax if I'm in a web environment). Once I have that established, anytime I need a new Session I can access my one already loaded instance of the SessionFactory.

# re: NHibernate on WCF

Left by Frank Quednau at 9/23/2008 3:22 AM
Gravatar
Hi there,
I came here over Jimmy Bogard's blog. I felt compelled to write some about this myself, since at a customer we are having just that scenario. You can see the post here : http://realfiction.net/?q=node/167 .
It is in parts quite similar to this post, but it should be noted that NHibernate has in-built support to provide a class that correctly scopes a session. It is done by implementing ICurrentSessionContext, associating the implementation with the session factory and from there on asking the sessionFactory in the style of sessionFactory.GetCurrentSession().
Cheers,
fq

# re: NHibernate on WCF

Left by Ravi at 1/23/2009 4:27 PM
Gravatar
Hi Donald,

Excellent article, I downloaded the code yesterday and started experimenting. I had few questions on how make a call from my WCF Service, how do we get the IsessionStorage? Would a new WcfSessionStorage() be enough. Using this method I noticed the Detach(InstanceContext owner) is not getting called. Can you please comment on how we can remove NhibernateContextExtension so that Detach method is called. Will there be an over head, if do the Commit by doing this NHibernateContext.Current().Session.Transaction.Commit();

Thanks,

Ravi

# re: NHibernate on WCF

Left by Ravi at 1/26/2009 10:21 AM
Gravatar
Hi Donald,

Have answered one of my own question here, I have added an event on Attach to call Detach when InstanceContext is closed.
owner.Closed += new EventHandler(delegate(object sender, EventArgs args)
{
this.Detach((InstanceContext)sender);
});
Can anyone answer my other question on how do we get the IsessionStorage? Would a new WcfSessionStorage()

Thanks,

Ravi

# re: NHibernate on WCF

Left by Rui Figueiredo at 6/4/2009 9:05 AM
Gravatar
Hi, I'm Starting with Nhibernate and WCF. I have done my first sample and it's works fine.
Could you tell me if there is something wrong is this code:

public static class NHibernateHelper
{
public static readonly ISessionFactory SessionFactory;
static NHibernateHelper()
{
try
{
Configuration cfg = new Configuration();
cfg.AddAssembly("SculptureSolution2.Entities");
SessionFactory = cfg.BuildSessionFactory();

}
catch (Exception ex)
{
throw ex;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}

[ServiceContract(Namespace = "urn:CustomersService", Name = "CustomersService", SessionMode = SessionMode.Allowed, ProtectionLevel = ProtectionLevel.None)]
public interface ICustomersService
{
[OperationContract(IsTerminating = false, IsInitiating = true, IsOneWay = false, AsyncPattern = false, Action = "GetAll", ProtectionLevel = ProtectionLevel.None)]
List<Customers> GetAll();
}

[ServiceBehavior(Name = "CustomersService",
Namespace = "urn:CustomersService",
InstanceContextMode = InstanceContextMode.PerCall,
ConcurrencyMode = ConcurrencyMode.Single )]
public partial class CustomersService : ICustomersService
{

public List<Customers> GetAll()
{
using (ISession session = NHibernateHelper.OpenSession())
{
return (List<Customers>)session.CreateCriteria(typeof(Customers)).List<Customers>();
}
}
}



Regards,

Rui Figueiredo.
Comments have been closed on this topic.