Ayende @ Rahien

It's a girl

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.

Tags:

Posted By: Ayende Rahien

Published at

Originally posted at

Comments

Scooletz
08/04/2011 10:43 AM by
Scooletz

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?

Ayende Rahien
08/04/2011 10:53 AM by
Ayende Rahien

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.

Ryan Heath
08/04/2011 12:11 PM by
Ryan Heath

HandleBeingMessage should be HandleBeginMessage

// Ryan

JarrettV
08/05/2011 09:02 PM by
JarrettV

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/

afif
08/08/2011 10:37 PM by
afif

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?

Ayende Rahien
08/10/2011 02:59 PM by
Ayende Rahien

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

viktor
10/25/2011 01:56 AM by
viktor

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

Ayende Rahien
10/25/2011 07:27 AM by
Ayende Rahien

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

Viktor
10/25/2011 08:48 AM by
Viktor

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 currentSession = new ThreadLocal();

    readonly IDocumentStore store;
    public RavenSessionMessageModule(IDocumentStore store)
    {
        this.store = store;
    }

    public IDocumentSession CurrentSession
    {
        get { return currentSession.Value; }
    }

    public void HandleBeginMessage()
    {
        currentSession.Value = store.OpenSession();
        currentSession.Value.Advanced.UseOptimisticConcurrency = true;
    }

    public void HandleEndMessage()
    {
        using (var old = currentSession.Value)
        {
            currentSession.Value = null;
            old.SaveChanges();
        }
    }

    public void HandleError()
    {
        using (var old = currentSession.Value)
            currentSession.Value = null;
    }
}
Ayende Rahien
10/25/2011 08:52 AM by
Ayende Rahien

Oh, yeah, I am sorry, you are correct, this is a bug in the code in the post, fixed now

Angel Blanco
11/06/2011 09:11 PM by
Angel Blanco

The code shown does not work, since nservice bus calls HandleEndMessage() before HandleError() and thus, even if an error occurs, changes would get saved.

Comments have been closed on this topic.