Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / debugging

Debugging NHibernate: Session Management

4.33/5 (4 votes)
13 Oct 2011MIT9 min read 33.7K   322  
This article explains the details of the session management in NHibernate. It shows you how, using the debugger, you may examine session properties and check if it's opened and closed in a desired way.

Introduction

NHibernate session is a gateway for all operations that you perform on your datastore. It also plays a role of the first-level cache, minimizing the number of roundtrips between your application and the database server. As NHibernate documentation states, session is "a single-threaded, short-lived object representing a conversation between the application and the persistent store". Pay special attention to the bolded words: single-threaded and short-lived. Single-threaded in web-development world basically means that you should not use the same session instance in two or more concurrent requests. Short-lived on the other hand instructs you not to "overuse" the session instance so it does not become a copy of the database. It's on the developer's shoulders to meet those requirements and implement a correct session management. Fortunately, there are several patterns already created for this purpose: session-per-request, session-per-conversation, session-per-thread, etc. with their implementation exemplars easily accessible in the Internet. But even if you take one of those examples and adapt it to your application needs, it may happen that the session does not behave in a way that you would expect: objects are not persisted, events are not fired or you get LazyLoadException. Those nasty bugs are usually quite tricky to resolve, especially in enterprise-level applications where objects lifetime is controlled by some kind of Inversion of Control container. In this article, I would like to show you how to find the exact moments when the session is created and destroyed (and by whom) and what information can be retrieved from its properties.

The usual approach in investigating NHibernate internals would be to turn on its fine-grained logging and check the log4net appenders output. However it's not the approach that I would like to describe in this post (please make a comment if you wish to read an article about detailed NHibernate logging). Today, we will focus on how to use the Visual Studio debugger to examine the session management. I prepared a sample application on which you may train the debugging process (details of the configuration are provided at the end of the post).

Prepare NHibernate Symbols

To be able to debug the NHibernate source code, the debugger must know how to find it. If you are using the 3.0.0 version of NHibernate, you may use http://www.symbolsource.org configuring Visual Studio to use their symbol store. If your NHibernate version is different, you need to either compile NHibernate on your own and reference the produced binaries in your project or make use of the sourcepack script. Here I will describe the second approach (especially because I'm the author of the sourcepack and I need to advertise it somehow ;)). To use sourcepack, you will need to install Debugging Tools for Windows (you may install them from the WDK image) and powershell v2. The next step is to download NHibernate binaries (you should have them already in your application bin folder) and NHibernate sources from their sourceforge page.

Now, we are ready to index the NHibernate symbol (PDB) files with the zipped sources. Open powershell console and type:

.\sourcepack.ps1 -sourcesRoot "d:\CSharp\NH\NH\nhibernate\" -sources 
	C:\src\NHibernate-3.1.0.GA-src.zip -symbols C:\temp\sample-app\bin 
	-archiverCommandPath C:\bin\7za\7za.exe -verbose

where:

  • d:\CSharp\NH\NH\NHibernate\ is a path to the source root on a computer where the NHibernate was built (you may check this path using srctool.exe or dbh.exe from the Debugging Tools for Windows - please have a look at my blog for usage examples)
  • C:\src\NHibernate-3.1.0.GA-src.zip is a place where you put the downloaded source codes
  • C:\temp\sample-app\bin is a place where you have your application binaries (DLLs + PDBs)
  • C:\bin\7za\7za.exe sourcepack requires a path to the 7za command that will be used by the debugger to extract the necessary source files (sourcepack release already contains the 7za.exe application so just copy it someplace)

After running the command, you should see some yellow (verbose) messages and NHibernate PDB files are now linked with the source code zip file. It's time to test if the debugger will be able to successfully extract them. For this purpose, you should again use srctool.exe (it's located in the srcsrv folder of the Debugging Tools for Windows installation):

srctool -l:*AbstractSessionImpl.cs NHibernate.pdb -x -d:c:\temp\nh-test

The output of the upper command should be:

c:\temp\nh-test\src\NHibernate\Impl\AbstractSessionImpl.cs

NHibernate.pdb: 1 source files were extracted

If you saw something different, you probably made some mistake in the indexing and you will need to rerun it (I know it's tedious and I have plans to make sourcepack usage a little bit more pleasant). If the previous sentence does not apply to your situation, you are ready to fire Visual Studio and start debugging NHibernate sources with your brand new symbol files :).

Break in Session Open

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.

Bound SessionImpl breakpoints

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

Breakpoint hit

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

Open session 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 in Session Close

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:

Frame1

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:

Frame2

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.

Frame3

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:

Frame4

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:

Frame5

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):

Frame6

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 flush process. 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 its main role is to close all opened connections and dispose internal NHibernate objects (as batchers, transaction wrappers, etc.).

Sample Code

To run the sample code, you will need SQL Server instance (Express edition is fine). The zip file contains a db.sql script with commands needed to setup a sample database. Please don't consider the sample code as an ideal MVC implementation - it contains as little elements as possible and all the code is placed in the App_Code directory just to make the deployment faster.

Remarks

In this article, I tried to show you how you can debug your NHibernate applications to check few of the session characteristics. The whole debugging process may become more complicated if your session contains hundreds of entities, or a problem of different nature appears (such as invalid transaction management). Nevertheless, I hope that you enjoyed reading this article and that it will help you with your future NHibernate struggles.

This article is based on posts from my blog http://lowleveldesign.wordpress.com. If you find it interesting, please pay me a visit and see what else is there:).

History

  • 12th October, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The MIT License