Debugging NHibernate: session management


Invalid session management is one of the common problems when using NHibernate and can lead to severe issues such as memory leaks or data inconsistency. In this post I am going to show you how to eliminate those pitfalls using the Visual Studio debugger. We will step through the process of opening and closing the session. We will also have a look at its properties in order to check if entities are correctly persisted. Let’s start then from breaking into the session open event.

Break into the session-open event

The best place to break into the session creation process is the SessionImpl constructor. You just need to choose New Breakpoint from the Debug menu and then Break at function… (or Ctrl + B) and in the Function box type NHibernate.Impl.SessionImpl.SessionImpl. The debugger will probably complain that it can’t bind the breakpoint at this time – just ignore this message and confirm that you want to set your breakpoint. It’s confusing that CLR uses .ctor as a constructor name when VS prefers the class name (although sometimes after binding the breakpoint it shows locations such as NHibernate.Impl.SessionImpl..ctor so don’t worry – everything is fine;)). One more caveat – in Visual Studio when you create a function breakpoint it will be set on all functions which names match the pattern that you provided. In our case we will end up with three child breakpoints as there are three constructs of the SessionImpl object (have a look at the first screenshot). There is no rule on when to create the breakpoint, but if you want to trace all the sessions you probably want to set it before even starting the debugging process.

Now, let’s start the debugger and try to do some actions in the application that will require a new session. While starting have a look at the breakpoints dialog (Debug -> Windows -> Breakpoints) and see how magically our breakpoints become available.

If you correctly prepared your symbols and sources you should now become quite happy seeing:

After the breakpoint was hit let’s examine the thread call stack:

I marked with a red rectangle two frames that actually tell us everything. The session was opened on ApplicationBeginRequest_ event by the default ISessionFactory implementation (SessionFactoryImpl). The great thing is that you can add this to the watch window and examine all the properties of the session. Also, you may set a breakpoint in the callstack and come back to ISessionFactory instance and check its properties too (like currentSessionContext that defines how long session will last and where it will be stored).

Break into the session-close event

In this paragraph we are going to step through the closing session process in order to find places where things may go off the rails. The closing process may be divided into two parts: flushing and actual closing. While flushing, NHibernate synchronizes the state of the session with the database, preparing an ordered collection of queries that must be then executed. We will start our analysis from placing an unresolved breakpoint on the NHibernate.Impl.SessionImpl.Flush. After firing the debugger (F5) our breakpoint should become alive and stop the execution at the following code:

The upper part of the picture contains the current call stack and the bottom part – the code. Normally you should first check if the Flush is called from where you planned it (in my case it was called while committing the transaction in response to the Application_EndRequest event – so no surprise here). We will be interested in the flushEventListener[i].OnFlush(new FlushEvent(this)) line as this is where the hard work is done. If you didn’t provide any own IFlushListener implementations to NHibernate the default one (DefaultFlushListener) will be used:

The marked lines are the most interesting. FlushEverythingToExecution checks the session objects states and prepares the queries, when PerformExecutions (as its name suggests) executes the queries against the database. At this moment it might be interesting to check what objects are stored in the session entity dictionary and what are their properties. To accomplish that expand the @event.Session object and choose PersistenceContext property of the SessionImpl object or just add ((NHibernate.Impl.SessionImpl)(@event.Session)).PersistenceContext expression to your watch window. Browse through the EntitiesByKey and CollectionsByKey properties.

The comment over FlushEntities explains exactly what this method does:

    
    // 1. detect any dirty entities
    // 2. schedule any entity updates
    // 3. search out any reachable collections
    private void FlushEntities(FlushEvent @event)
    {
    // other code goes here ...

In the code below you can see that it iterates through the EntityEntries collection firing OnFlushEntity event for each entity with the correct status:

The selected line above might be an interesting place for a conditional breakpoint filtering entries that we are interested in, like entry.Id.Equals(1) or entry.EntityName.Equals("NHibernate.Example.Web.Models.Item"). Our next place to visit will be DefaultFlushEntityEventListener and its OnFlushEntity handler:

The first red rectangle selects the code that checks the entity status – whether its properties had changed from loading or it was deleted or it’s a new entity. You may debug this code if you find that your object is not being updated, but before you get your hands dirty you may first check if the SessionImpl.ActionQueues is being updated by this method. ActionQueues as its name suggests contains actions that must be executed in order to synchronize the session with the database. They are grouped into queues based on their category: insertions, updated, deletions, collectionCreations, collectionUpdates, collectionRemovals. At this point you may wish to check if those collections contain a valid number of elements (or even the correct number of dirty properties). After filling the action queues there comes a time to translate them to SQL queries. This is done in the PerformExecutions method of the DefaultFlushingEventListner (or rather AbstractFlushingEventListener):

Internally ExecuteActions calls NHibernate.Engine.ActionQueue.ExecuteActions method which iterates through all the actions in all queues calling their Execute method, which internally prepares SQL queries (Persister‘s Update method) and (if configured) puts them into batches or executes immediately.

This would be the end of the description of the session flushtprocess. There is one more thing that needs some explanation – session’s closing. Fortunately for you, probably already bored readers;), this method is fairly simple and it’s main role is to close all opened connections and dispose internal NHibernate objects (as batchers, transaction wrappers etc.).

I hope that this post revealed some of the mysteries behind NHibernate session management and will help you in your future struggles with NHibernate.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s