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.