session-per-request implementation for WCF, NHibernate, and Ninject

3.5k views Asked by At

I am trying to implement a session-per-request model in my WCF application, and I have read countless documents on this topic, but looks like there is not a complete demonstration of this. I actually came across some very useful articles such as this one:

NHibernate's ISession, scoped for a single WCF-call

but these are all from the old days when NHibernate and Ninject did not have WCF specific implementations, therefore they achieved what I need by implementing their custom service providers, etc. Since both Ninject and NHibernate have WCF support now, I want to keep things consistent by using their modules, but I ended up here...

The basic setup and flow should be something like this:

  1. Set CurrentSessionContext to WcfOperationSessionContext in nhibernate configuration
  2. On service start, begin request, or anywhere around the init time, open session and bind it to the current context
  3. Repositories get the current session instance using SessionFactory.GetCurrentSession() method
  4. Unbind and close session at the end of the lifecycle

My initial problem was that I wasn't able to access to the wcf lifecycle to handle my bindings. After digging into the ninject code a bit, I managed to hook my methods to ServiceHost's Opening / Closing events without changing much, but then I wasn't able to access to the OperationContext since it is thread-static.

Later I tried enabling asp.net compatibility and using Application_BeginRequest and Application_EndRequest, and it looked very promising, but I don't think that's the best solution since I should be binding stuff to the service instance, rather than the http request.

Has anyone ever achieved this using ninject's built-in wcf extension libraries? Or any ideas on what I might be doing wrong?

3

There are 3 answers

2
Sly On BEST ANSWER

I have implemented per request session lifetime with the help of IDispatchMessageInspector. Probably you could implement custom lifetime manager for Ninject to achieve per web request.

0
Shiraz Bhaiji On

You may be able to do it by using the extension points provided in the IInstanceContextProvider Interface.

0
Daniel Marbach On

Hy

You can do the following:

public class DomainModule : NinjectModule
{
    private const string RealSessionIndicator = "RealSession";

    private readonly ProxyGenerator proxyGenerator = new ProxyGenerator();

    public override void Load()
    {
        this.Bind<ISession>().ToMethod(ctx => ctx.Kernel.Get<ISessionFactory>().OpenSession())
            .When(r => r.Parameters.Any(p => p.Name == RealSessionIndicator))
            .InRequestScope();

        this.Bind<Func<ISession>>().ToMethod(ctx => () => ctx.Kernel.Get<ISession>(new Parameter(RealSessionIndicator, (object)null, true)));

        this.Bind<ISession>()
            .ToMethod(this.CreateSessionProxy)
            .InTransientScope();

        this.Bind<ISessionFactory>().ToMethod(ctx => ctx.Kernel.Get<Configuration>().BuildSessionFactory()).InSingletonScope();
    }

    private ISession CreateSessionProxy(IContext ctx)
    {
        var session = (ISession)this.proxyGenerator.CreateInterfaceProxyWithoutTarget(typeof(ISession), new[] { typeof(ISessionImplementor) }, ctx.Kernel.Get<SessionInterceptor>());
        return session;
    }
}

public class SessionInterceptor : IInterceptor
{
    private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    private readonly Func<ISession> sessionProvider;

    public SessionInterceptor(Func<ISession> sessionProvider)
    {
        this.sessionProvider = sessionProvider;
    }

    public void Intercept(IInvocation invocation)
    {
        try
        {
            var session = this.sessionProvider();
            invocation.ReturnValue = invocation.Method.Invoke(session, invocation.Arguments);
        }
        catch (TargetInvocationException exception)
        {
            Log.Error(exception);
            throw;
        }
    }
}

With that you can use everywhere ISession without caring about the details. You can edit InRequestScope with InScope(ctx => OperationContext.Current) to use WCF scope