powered by myself

Rich-client NHibernate session management

I’ve been struggling with this subject for quite some time now, and I’ve just seem to found the ‘sweet spot’.

Before we start, I need to describe some facts about the target application:

  • rich-client: this approach will work the same for a Winform application as for a WPF application.
  • multi-screen UI: it could be multi-form or tabbed or MDI… the important thing here is that you “will” have several screens opened at the same time.
  • application uses an Inversion of Control container for wiring up dependencies among other useful things (I’m using Castle Windsor/Microkernel)

Ok.

Before we continue, I recommend that you read the work of Sebastian Talamoni, that although not finished yet, covers a lot of ground.

Basically there are 4 options for NH session management on a rich-client environment:

  1. Session per application (per thread)
    • the simplest approach
    • only 1 session opened for the whole application lifecycle
    • lots of problems: cache size, stale data due to other users, unrecoverable exception, etc.
    • Fabio Maulo calls this pattern TIME BOMB
  2. Session per screen
    • 1 session opened for each form/tab/view/whatever
    • implies multiple concurrent sessions
    • since a single screen could be left opened for quite some time, this approach is almost equally susceptible to stale data as the previous one
  3. Every data access – Fine-grained sessions
    • every repository/DAO opens and closes a new session each time that a DB access is requested.
    • loss of some useful NH features like lazy loading and 1st level cache… not worth it.
  4. Session per use-case
    • also implies multiple concurrent sessions
    • this seems to be the right choice, however it is the most difficult to implement

Per-thread session management

Options 1 and 3 are easily implemented using some kind of thread-static session manager. Examples of this are Rhino.Commons and Castle NHibernate Integration Facility.

Each repository/DAO gets an instance of the a global “session manager” and uses it to get hold of the right session:

  • if 1 session per application is used, this “global” session is return by each call to SessionManager.OpenSession()
  • if fine-grained sessions are used there are two possibilities:
    • we are in the scope of another session… the outer session is returned
    • we are not in the scope of another session… a new one is created

The easy thing about this is that each repository/DAO can access to the “session manager” through some static accessor, or even better, they can receive a singleton (as in singleton lifestyle) instance as a parameter of its constructor, which enable us to use an Inversion of Control container to wire up these dependencies and ease the unit testing of these components (no statics = easier testing).

public class Presenter {

   private IBlogRepository blogRepository;

   public Presenter(IView view, IBlogRepository blogRepository)
   {
       this.blogRepository = blogRepository;
   }

   ...

}

public class BlogRepository : IBlogRepository

   private ISessionManager sessionManager;

   public BlogRepository(ISessionManager sessionManager)
   {
       this.sessionManager = sessionManager;
   }

   public Blog Get(int id)
   {
       using (ISession session = sessionManager.OpenSession()) {
           return session.Get<Blog>(id);
       }
   }
}

This pattern is suitable for a web application, where the context is usually given by the current request, but in a rich-client application it would imply that two opened screens (different contexts) would share the same session manager and therefore the same session when they intend to do some data access at the same time.

Contextual session management

Options 2 and 4 require another approach since they imply several simultaneous sessions through the lifecycle of the application. We can no longer have a static or singleton session storage from where to get the right session for each thread… we need some kind of “contextual session management” where each screen/use-case is treated as a different context and provided with sessions accordingly.

This don’t seem so hard, right? We can just do something like this for option 4:

public class Presenter {
   private ISessionManager sessionManager;
   private IBlogRepository blogRepository;

   public Presenter(IView view)
   {
       sessionManager = SessionManagerFactory.CreateNewContext();
       blogRepository = new BlogRepository(sessionManager);
   }
}

The implementation of BlogRepository in this case is the same as before.

At first sight this seems like a nice way to define contexts, but that is until you realize that you are making very hard to unit test this class (hardcoded dependencies) and you are neglecting the help of the IoC container to wire up things.

Imagine that now, instead of BlogRepository we need something like AggregatorService that depends on BlogRepository, UserRepository and CommentService, and CommentService depends on UserRepository and CommentRepository. In this case, you’d need to create the session manager and then to instantiate every one of this components passing the right arguments to them…. this seems too much work for me.

Managing the dependencies yourself is not fun, and your IoC container enjoys doing it for you anyway.

Maybe something can be done using some kind of service location and passing the current context down the chain, like this:

container.Resolve<IBlogRepository>(With.Dependency<ISessionManager>(sessionManager)); // imaginary syntax
container.Resolve<IAggregatorService>(With.Dependency<ISessionManager>(sessionManager)); // imaginary syntax

but there is an easier way, let me introduce you to the…

Contextual Session Manager

This is an extension to the NHibernate Integration Facility of the Castle project that allows me to control the scope of my session and do not impose a global per-thread scope.

The solution is integrated by the following components:

  • ContextualNHibernateFacility: inherits from NHibernateFacility and all it does is to replace some of the standard components registered by the original facility specifying the right Lifestyle for the new ones.
  • ContextualPerThreadStore: inherits from AbstractDictStackSessionStore and provides a contextual session storage where there will be a different session for each instance of this class for each thread (tricky). The original CallContextSessionStore uses the call-context to store sessions, that means that two different instances of CallContextSessionStore will return the same session for the same call-context… this behavior is the one that I didn’t like.
  • ContextualSessionManager: just inherits from DefaultSessionManager to specify a custom lifestyle (see below) to be use by Microkernel to manage this component.
  • ResolutionContextLifestyleManager: inherits from AbstractLifestylemanager. This is the “thing” that delimits our contexts. For more information go here.

This set of components allow me to use the container to resolve presenters, ViewModels, forms, or anything else that I’d like to use to delimit a context, like this:

var presenter = container.Resolve<IBlogPresenter>();

then, every required dependency (including the NH session manager) is resolved within the same context, all of this without even making any component aware of the existence of such grouping.

It is all handled by the container, which is exactly what I was looking for.

Sample Project

I’ve put together a small spike to illustrate these concepts. Maybe a more complex application would be more suitable to present some things, but for now this is all I’ve done.

The sample solution uses NHibernate, Windsor, NH facility, ATM (automatic transaction management) and a simple WPF UI using Caliburn’s Action support.

If you’re using SQL Server Express you just need to create an empty DB called “test”, otherwise just modify the settings in App.config to suit your needs. The tables and initial data are created by the application at startup.

You can download the sample here.

Improvements

One obvious thing that is left out here is the capability to resolve more than one root component for a given context (two repositories in two different presenters that get the same ISessionManager) but that is something that could be added with no problem.

There are probably a lot of scenarios where the approach described here does not apply… I’ve just implemented a solution to my problem that works well for me and I think that maybe someone else can benefit from this.

I would like to know what do you think about this approach.


Kick It on DotNetKicks.com

11 Comments to Rich-client NHibernate session management

  1. Anonymous's Gravatar Anonymous
    March 24, 2009 at 6:38 am | Permalink

    Hi German,

    This is a very useful, high-quality post. Thanks.

  2. JeffJ's Gravatar JeffJ
    March 24, 2009 at 8:53 pm | Permalink

    Nice article. I’ve been working with the same problem, but in a little different light. The app I have has a FileSave metaphor and need to defer the database updates. The Level1 Cache seems to be just the ticket, but this seems to limit my threading opportunities and I get nervous implementing ‘time-bomb’ patterns :)
    With the UnitOfWork\PerUseCase\PerScreen doesn’t stale data seem like a problem, particularly if one screen updates, while another use case won’t see that update?

    I’ve thought about doing something wacky with a customized level2 cache but don’t know if that is even within the realm of possibility.

  3. Germán Schuager's Gravatar Germán Schuager
    March 25, 2009 at 1:13 am | Permalink

    Hi Jeff.
    I don’t think that I fully understand your question. Can you rephrase it?
    However I’d like to point some things:
    - The synchronization between screens should have nothing to do with NH. I use an eventing system similar to the one in Prism.
    - I wouldn’t use the 2nd level cache for anything else than performance optimization.

  4. Fabio Maulo's Gravatar Fabio Maulo
    March 25, 2009 at 3:51 am | Permalink

    In winForm/WPF I prefer CpBT where the infrastructure matter is resolved by AOP in a high level tier (model/service or presenter)and Dao/Repository don’t know nothing about who are managing the nh-session (DAOs/Repository should know only ISessionFactory).
    Example is available in uNhAddIns and posts are available in my blog.

  5. nieve's Gravatar nieve
    April 11, 2009 at 8:10 pm | Permalink

    Hi German,
    I was trying to get the hang of that solution, but just didn’t get it. How and where exactly do you decide to open and/or close the session and/or transaction?
    If I got it right, you wrap a service/model/presenter method in a using (unitOfWork.Start()); what about a couple of methods you want to wrap in a conversation the way Fabio does it?

    Otherwise I’m trying to understand the difference between the Spring Transaction attribute wrapper that opens the session and flushes it and commits the tran when all is good and rollbacks transaction when an exception is caught.

  6. nieve's Gravatar nieve
    April 11, 2009 at 8:22 pm | Permalink

    Erm, just to clarify something- are you opening and closing a session for each DAO/Repository method by default? Sorry, am a bit lost here, not sure I understood the whole mechanism- too tired and other lame excuses ;)

  7. Germán Schuager's Gravatar Germán Schuager
    April 11, 2009 at 9:25 pm | Permalink

    Hi nieve,
    The actual opening and closing of the NH session is handled by the Castle NH Facility: http://castleproject.org/container/facilities/trunk/nhibernate/usingit.html#SessManager
    The main advantages that it provides are integration with the rest of the Castle stack, ATM support (declarative transaction delimitation), nested calls to ISessionManager.OpenSession() will return the same session, etc.

    Given one of the repository methods, the OpenSession method will create a new session if there was none created before in the same context, or it will return the current session of the current context if it already exist (maybe created by unitOfWork.Start().

    In other words, if you wrap several methods in a single unit-of-work, each of them can manage their respective transactions but all of them will share the same instance of the NH session –> session-per-conversation.

    The good thing here is the delimitation of different contexts, because this allows you to implement even simultaneous long conversations in a rich-client in a relatively easy way (not shown in the example)

  8. nieve's Gravatar nieve
    April 12, 2009 at 8:50 am | Permalink

    Well, I suppose because I don’t have a thorough castle background it’s a bit more difficult for me to see exactly how all this is done, esp. when some of the examples are missing (the long conversations you’ve mentioned?).
    Anyway, it looks interesting and I would have loved to see a more complex example just to get the hang of what you’re doing here; however, I still have the feeling this is very similar to Fabio’s Conversation attributes, only he’s using AOP/attributes which I personally prefer as well as a syntax/names that are more user-friendly/readable/make more sense. I’d strongly suggest you to go have a look at what he did with the conversation attributes. On top of that, his solution can be easily imported into other AOP/IoC frameworks such as Spring.Net, which is a great plus…

  9. Germán Schuager's Gravatar Germán Schuager
    April 12, 2009 at 1:36 pm | Permalink

    I have to admit that my implementation is too tied to Castle, but that was what I was needing when I came up with it. I haven’t used Spring.net so I don’t know how hard would be to port these concepts to that IoC framework.

    Anyway, I owe to myself to take a look at Fabio’s work with CpBT. When I get some time I will do it and post my impressions here.

  10. Karron Qiu's Gravatar Karron Qiu
    April 23, 2009 at 6:13 am | Permalink

    I’m very interested in your great post, I have the trouble about session management in wpf application. But I can’t download the sample. Would you please send the sample project to my mailbox( karronqiu AT gmail.com )? I’m from China, the fxxking GFW block mediafire.

    Thank you very much.

  11. Ben Gribaudo's Gravatar Ben Gribaudo
    October 19, 2010 at 8:44 pm | Permalink

    The thoughts you shared on how to automatically contextualize ISessions are helpful. Thank you!

Keep in touch

Categories