问题
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:
- Set CurrentSessionContext to WcfOperationSessionContext in nhibernate configuration
- On service start, begin request, or anywhere around the init time, open session and bind it to the current context
- Repositories get the current session instance using SessionFactory.GetCurrentSession() method
- 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?
回答1:
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.
回答2:
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
回答3:
You may be able to do it by using the extension points provided in the IInstanceContextProvider Interface.
来源:https://stackoverflow.com/questions/4867457/session-per-request-implementation-for-wcf-nhibernate-and-ninject