RavenDB Session Management with NServiceBus
I got a few questions about this in the past, and I thought that I might as well just post how I recommend deal with RavenDB session management for NServiceBus:
public class RavenSessionMessageModule : IMessageModule { ThreadLocal<IDocumentSession> currentSession = new ThreadLocal<IDocumentSession>(); IDocumentStore store; public RavenSessionMessageModule(IDocumentStore store) { this.store = store; } public IDocumentSession CurrentSession { get{ return currentSession.Value; } } public void HandleBeginMessage() { currentSession.Value = store.OpenSession(); } public void HandleEndMessage() { using(var old = currentSession.Value) { currentSession.Value = null; old.SaveChanges(); } } public void HandleError() { using(var old = currentSession.Value) currentSession.Value = null; } }
When any of your message handlers needs to get the session, you need to wire the container in such a way that it would provide the RavenSessionMessageModule.CurrentSession to it.
Comments
I do prefer to use one message module for all the object with PerRequest lifecycle. What if you needed another one, you'd copy the module?
Scooletz, That depend on what scenarios you have. In our case, we need to call SaveChanges if it didn't error. The NSB API doesn't give us much other choices.
HandleBeingMessage should be HandleBeginMessage
// Ryan
Thanks, fixed
I was worried when I first saw this post but after re-reading the following it seems ok:
"The biggest challenge relates to not having a “final”-type method. While message modules do expose HandleEndMessage and HandleError, we have no way of knowing which will be called last. True, HandleError will always be called after HandleEndMessage, but we have no way to determine if HandleError will be called once HandleEndMessage is invoked. Note that HandleEndMessage is always called, even in error conditions."
http://blog.jonathanoliver.com/2010/04/extending-nservicebus-thread-specific-message-modules/
would it make sense if the message handlers get injected an interface to an IDocumentSession that does not have functionality to open, close, commit, dispose etc. for e.g. an ICurrentDocumentSession or something like that. that way we are more explicitly modelling the fact that the session instance injected to message handlers is only providing a window into an open session... your thoughts?
Afif, That makes absolutely no sense, I am afraid. The only thing in the IDocumentSession that relates to the lifetime of the session is the Dispose method, and trying to create a new interface just to hide that seems silly
what do you think about this approach? https://github.com/NServiceBus/NServiceBus-Contrib/blob/master/src/SagaPersisters/RavenDB/NServiceBus.SagaPersisters.RavenDB/RavenDBMessageModule.cs
btw. i think "RavenSessionModuleStore store;" should be "IDocumentStore store;" viktor
Viktor, I don't see any RavenSessionModuleStore there, I see the DocumentStoreFactory, which seems to be mostly about preserving the current session, which is fine
sorry for my bad explanation. with "RavenSessionModuleStore store;" i mean that your RavenSessionMessageModule will not compile, because "RavenSessionMessageModule store;" should be "IDocumentStore store;" or see below Viktor
public class RavenSessionMessageModule : IMessageModule { readonly ThreadLocal<IDocumentSession> currentSession = new ThreadLocal<IDocumentSession>();
Oh, yeah, I am sorry, you are correct, this is a bug in the code in the post, fixed now
The code shown does not work, since nservice bus calls HandleEndMessage() before HandleError() and thus, even if an error occurs, changes would get saved.
Comment preview