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.