How to review NHibernate application
A few hours ago I completed a code review of an application using NHibernate. This is not the first time I am doing such a thing, of course, and I noticed that there are quite a few areas where I tend to have comments in such code reviews.
The following is based on several such code bases that I went through, and contains a partial list of things that you need to watch for.
Mapping
- The recommended practice is to have each mapping file contain a single class. Having a single mapping file contain multiple classes make it harder to find the matching file, make source control harder to deal with and means that you have to wade through a lot more XML than is healthy for you.
- Do not specify things in the mapping that you don't have to. Let NHibernate figure out what it can. When you need to change things, you'll be able to do so more easily.
- If you have logic for selecting which mapping files goes to which session factory, base this logic on easy to follow conventions, such as directory structure, not things that are hard to follow (like processor directives in the XML). In general, avoid having a whole pile of mapping in a single directory, it makes it confusing to deal with.
Session Management
- If you need to call sessionFactory.OpenSession(), stop and rethink what you are doing. In most scenarios, what you should prefer is a contextual session. This allows you to play some very interesting games (such as multi tenanting the application without letting the application know about it).
- Be aware of the session that you have. If you do have manual session management, just about the worst thing you can do is to open & close the session per method call.
In particular, this is a worst practice, in my eyes.
public void Save(Customer customer) { using(var session = SessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { session.Save(customer); tx.Commit(); } }
Transaction Management
- Use transactions, always.
- Set the Session Flush Mode to Commit, in order to help enforce that.
- When you are using a transaction, always wrap it in a using statement.
- Don't forget to commit :-)
- Transactions should be opened at the service boundary.
NHibernate Internals Usage
- Make use of the facilities that NHibernate gives you, but ensure that they are encapsulated well. For example, if you are using IUserType, neither the entity where it is used or the calling code should be aware of that in any way.
- NHibernate has quite a few extension points that you can use, be aware of them, and the freedom that they give you. However, you should also consider that they are there for special cases, and it is usually better if you can avoid being a special case. I am not saying that you should not extend NHibernate or use the facilities that it gives you to do so. What I am saying is that anything that I have to dive deeply into NHibernate from the application code, I stop and rethink the problem.
- In particular, you entities should never make any call to the session that created them in order to modify their own state.
Set based thinking
- NHibernate is OR/M - That is, it bridge between a database and the domain model. There is no good reason to forget that the database is there, and not use it.
- In particular, things such as filtering and ordering are generally best done at the database level.
- Overall, remember that the DB is well suited for such tasks, and that doing them at the application level will be a major pain and a hot spot for maintainability issues.
Select N+1
- Be aware for what is the access pattern of your code, and take measures to reduce the amount of calls to the database.
- There is no way NHibernate can be fast if you make it call the DB in a loop, where N is any significant number.
Comments
Hi Ayende,
Maybe you could you expand on that sometime. A topic for a future post perhaps, it would be really interesting to see how you would architect a web application with a contextual session management that supports multi tenancy that is a scalable keeping in mind the possiblity of web farms / load balancers.
-Brian Chavez
Hi,
the docs I found on contextual session talk about "GetCurrentSession" and how you can implement the behaviour behind this. Would it be OK for you to point me to why this approach is preferrable? (I didn't understand the multi-tenanting bit)
e.g. If I have a call to a WCF service and I use some repository, abstracting the NHibernate bits, is it not ok to get a session when the repository is activated and discard it once the repository is also discarded?
I am still a beginner with Nhibernate and I saw quite a lot of code on the Internet that looks like your worst practice...it would be interesting to hear why it is bad.
Cheers
I consider myself as a beginner in NHibernate, and I've some questions regarding the tips you've posted here:
In which case(s) would you have multiple sessionFactories in one application ? And why would you benefit from that ? Wouldn't it make your app more complex / harder to maintain ? (I think you've to ask yourself the question everytime: what sessionfactory do i need to be able to persist an object of type xyz ? )
With contextual session mgment, you mean that it is the consumer of your 'domain' (be it a service interface or your winforms app itself) that should decide when a session is to be opened and closed, or when a transaction should start ? (That's the part of your app which knows about the context; the context-is-king-idea ? )
@Frank Quednau : What Ayende wants to say -imho- is that you should start a new session in every method of your repository.
Why not: your repository doesn't know if there are other repository-methods that are to be executed which could use the same session, or even worse, it could be that you want to save numerous other objects in the same transaction.
When you open a session or start a transaction in your repository method, you are in no way able to save multiple objects in one transaction.
Therefore, it is the 'consumer' of the domain model (the repository is part of the domain model) which should decide when to open and close a session, and when to start / commit a transaction.
You could ofcourse start a session in your client, pass that session to instances of the repository, and let the repository work with that session.
Ayende, do you happen to have (or know of) a simple sample NH project that you would consider best practice that people (namely me) could refer to when getting started with NH?
I've been using it for a couple of months now and still struggle with the numerous ways there are to do things but not knowing which is the best approach to follow.
Dan
Ayende, I would love to see you do some posts addressing the above questions. I think a lot of people could benefit from some more information.
I would like to echo Danny's question - for those of us who are not seasoned NHibernate users (but would like to understand how to use it correctly) a best practices example no matter how simple would be worth its weight in gold. Also - is anyone aware of a tool that can generate both NHibernate mappings / Database Schema Sync Scripts from a metadata model? :)
@DannyT & Nathan: I'm using the NHibernate Ref. Guide (which you can find on the nhibernate webpage). I've also used the article by Billy McCafferty on CodeProject as a reference.
"Don't forget to commit :-)"
I thought Transactions were implicitly committed upon disposal if no exception was thrown and it was not explicitly rolled back?
I knew something was implicit, but I had it backwards, rollback is implicit if not explicitly committed.
Thanks Frederik, that is an awesome article. The article is here: http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx. That author also has a framework cooking on CodeProject that incorporates NHibernate and MVC and emphasises DDD - it is here: http://www.codeplex.com/SharpArchitecture. So much to learn, so little time :)
The article on codeplex seems interesting as well. However, I'd like to see an example app of a Winforms application as well.
Im struggling a bit on how to do session managment there ...
I use long sessions, but ... do you keep the session connected to the DB for the duration of the session ?
If you don't use long sessions, you have a chance that unnecessary update statements are issued to the DB (or maybe I'm doing something completely wrong ... ).
I'd like to see some input 'bout that.
Unfortunately, the "first application" in nhibernate faq ( http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/04/01/your-first-nhibernate-based-application.aspx ) does the two things you discourage for session management. It's hard to find a good Nhibernate/ActiveRecord example to follow without findind someone else discouraging some of the practices shown in it.
alberto,
That is a problem in general, because samples are inherently trying to simplify everything except what they are demonstrating.
@alberto That sample has to be simplified. Otherwise you'd scare a first-timer away from NHibernate, and you don't want that :P
I understand, but I would not expect find a "worst practice" in an example trying to show how to use a tool. And if there is one, it should be clearly stated. Otherwise, when I read it I think "this is the way it should be done". For the very same reasons that I wouldn't teach someone how to write sql sentences by doing string concat without putting a BIG, blinking warning.
That said, I'm still struggling for a good example that doesn't force me into a bunch of totally unrelated stuff, like ioc or mvc (ok, I know it's not unrelated, but it should not be there adding complexity to the example).
The best one I have found so far is Exesto, but if it's so hard to find a decent example, I can't imagine how difficult it will be for advanced topics. The nhibernate tutorials @ http://www.hibernate.org/365.html are discouraging, with broken or outdated links.
Comment preview