session-per-request implementation for WCF, NHibernate, and Ninject
Asked Answered
B

3

11

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?

Basenji answered 1/2, 2011 at 20:10 Comment(0)
L
2

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.

Lauer answered 2/2, 2011 at 7:56 Comment(2)
You were right, thanks. I was reading about IDispatchMessageInspector and I found this implementation: igloocoder.net:8443/svn/IglooCommons/trunk/src/… With some minor tweaks, I was able to make it work.Basenji
The link is, by now, unfortunately broken. :(Expositor
P
1

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

Parley answered 2/6, 2011 at 14:21 Comment(0)
C
0

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

California answered 2/2, 2011 at 7:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.