I've been using NHibernate almost exclusively for the last 2 years for all my data access needs. That in an of itself is not remarkable. What does feel remarkable, to me at least, is that I don't really do any web development. Or maybe that isn't remarkable. I'm not sure.
What I am sure about is that most of the documentation and examples that you commonly encounter with NHibernate are about Blogs, or web stores, or something equally non-desktop based. Maybe as a direct result of this, it took me a good six months of banging my head against the NHibernate learning curve before everything about it really clicked. So, without further ado, here are some things I've picked up.
ITEM THE FIRST: An ISession and a database transaction are not the same thing. An ISession and a database transaction are not intended to be the same thing. Sessions should open and close transactions every time they interact with the database, but you should never, ever open a transaction and keep it open the entire time the ISession is alive. This will result in table locks galore and more headaches than you will know what to do with.
That being said, ISessions make sense to hold open in relation to a form- it enables first level caching as a performance booster and most users will intuitively understand that they will sometimes have to close and open or reload a form to see updates
.
Unfortunately, this is not one of those problems that single developer testing on a development database is going to enlighten you to. Keeping a Transaction open the entire time a form is open so you can quickly and easily roll back changes might seem like a good idea on the surface! IT IS NOT.
ITEM THE SECOND: Just because you have "Lazy" Loading doesn't mean you get to be. In my specific application domain, I have several high level domain wide objects or collections of objects that almost everything is related to, like "Company" and "Employee". In earlier versions of my domain, I had these objects related though object references with all the child objects that belonged to them. This resulted in some hideous N+1 problems.
For the uninitiated, an N+1 problem is when, for every result returned in a query, you run an additional query to fetch related data. If I wanted to show a list of some child object, and include on that list the name of employee who was the owner of that object, I face planted on an N+1 problem. N+1 is probably the biggest trap to avoid when working with NHibernate, because it will
destroy your performance.
The internet will probably point you towards a more web oriented solution to these issues- which is to tweak your query plan, or your mapping files to perform batch selects. There is nothing wrong with these approaches, but they aren't ideal. You need to know ahead of time what kinds of queries you are going to run to tweak your query plan, and if you edit the mapping files you may introduce some unintended consequences. But we don't have to deal with unknowns in terms of the life span of the assembly, nor do we have to worry about extending the length of our first request and creating uneven performance, so there are other options.
The plan I settled on was to pre-load these kinds of things. On application_load, I run a series of queries which loads the non-changing parts of the domain into some application static objects. The child objects that refer to these get loaded with only a integer pointing to the unique ID of the static object in a private field from NHibernate, and then I have a simple property that wraps up access to this to make it transparent to the user that I am, in fact, not dealing with these things on a purely object level.
Not exactly ground breaking, but not exactly something that naturally occurs to people, or me at least.