Nhibernate single ISession per request (GetCurrentSession()) over WCF (via config behavior).

January 18th, 2012  |  Published in NHibernate  |  2 Comments

To get this to work you need to implement the IDispatchMessageInspector and the IServiceBehavior interfaces.

You also need to set-up a BehaviorExtensionElement to add into the behaviors for your service.

To get the GetCurrentSession() to work you need to complete the following steps:

  1. Create a BehaviorExtensionElement.
  2. Implement IDispatchMessageInspector and the IServiceBehavior interfaces.
  3. Amend your config (NHibernate)
  4. Amend your web.config (WCF)

The BehavoirExtensionElement is used to fire the IServiceBehavior implimentation (although in my case I have one class that implements both the IDispatchMessageInspector and the IServiceBehavior interfaces).

The BehaviorExtensionElement is very simple to implement:

public class MyBehaviorExtension : BehaviorExtensionElement    {
    public override Type BehaviorType
    {
        get { return typeof(MyInceptor); }
    }
    protected override object CreateBehavior()
    {
        return new MyInceptor();
    }
}

Now when the behaviour fires the MyInceptor we want to add a call to our IServiceBehavior to we can add our IDispatchMessageInspector to the collection of MessageInspectors for the service endpoint.

        public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
            //have no need to implement this method.
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            //This is where the Inspector gets hooked to all the endpoints.
            if (serviceHostBase != null)
            {
                for (Int32 i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
                {
                    ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
                    if (channelDispatcher != null)
                    {
                        foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                        {
                            //Add this class to the collection.
                            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
                        }
                    }
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            //have no need to implement this method.
        }

In my case I add do endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this); as my class contains the implementation for IDispatchMessageInspector too.

But if that was in another class then I would change it to look more like this:

MyIDispatchMessageInspector myIDispatchMessageInspector  = new MyIDispatchMessageInspector(); 

endpointDispatcher.DispatchRuntime.MessageInspectors.Add(myIDispatchMessageInspector);

As you can see the only method that we need to do anything with is the ApplyDispatchBehavior method.

Next we need to setup our IDispatchMessageInspector.

This is reasonably simple, IDispatchMessageInspector defines two methods – AfterReceiveRequest and BeforeSendReply.

So something like this will setup our CurrentContextSession (used by GetCurrentSession()) :

//get your persistence helper / manager
private static PersistenceHelper persistenceHelper = new PersistenceHelper();

        private static readonly ISessionFactory _sessionFactory = persistenceHelper.SessionFactory;

        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            //bind the context.
            CurrentSessionContext.Bind(_sessionFactory.OpenSession());
            return null;
        }

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            //unbind the context.
            var session = CurrentSessionContext.Unbind(_sessionFactory);

            //dispose of the session.
            session.Dispose();
        }

Now the session is setup when the request comes in and closed before the reply is sent – Happy days.

The NHibernate config change:

Add something like :

cfg.CurrentSessionContext<WcfOperationSessionContext>()
                   .Proxy(p => p.ProxyFactoryFactory<ProxyFactoryFactory>())
                   .SessionFactory()
                   .GenerateStatistics();

When you are configuring nhibernate.

The important part for what we are trying to achieve is the cfg.CurrentSessionContext<WcfOperationSessionContext>().

This can be setup via the web.config but I have not tried it, if you are interested the tag you need to add is<property name=”current_session_context_class”> WcfOperationSessionContext</property>.

Like I say I have not tested the web.config setting but I believe it should work.

Now for the web.config change:
After the <behaviors> section of your WCF config add:

<extensions>
    <behaviorExtensions>
        <add	 name="MyBehaviorExtension "	 type="Myproj.MyBehaviorExtension, Myproj, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </behaviorExtensions>
</extensions>

and add:
<MyBehaviorExtension />
to the service behavior you want to use it on.

You should now find that you have one session per request, which is good for performance (although creating new ISessions does not seem to be all that slow) but better than that it means you no longer need to manage the sessions, you can just call MyPersistenceHelper.GetCurrentSession();

That’s all for now.

Responses

  1. Bert Verbeecke says:

    January 15th, 2013 at 2:48 pm (#)

    This seems to be the only article on use WcfOperationSessionContext. Sadly enough it is not clear enough to implement.

  2. David says:

    February 26th, 2013 at 8:20 pm (#)

    Hi Bert,

    Im sorry you didn’t find my post clear, I will try and tidy the post and code up a bit to see if I can make it any easier to follow.

    If you could let me know at what point it was unclear that would be a great help.

    Thanks,
    David

Leave a Response