I often see code that looks a bit like this:
public ActionResult UpdatePost(int id, FormCollection form)
{
// we won't worry about where _nhibernateSession
// comes from for this example.
var post = _nhibernateSession.Load<Post>(id);
post.Title = form["PostTitle"];
post.Body = form["PostBody"];
_nhibernateSession.Update(post);
_nhibernateSession.Transaction.Commit();
}
Can you see what's wrong here? The call to _nhibernateSession.Update(post)
is useless.
Why? Because the NHibernate session already knows that the object is being changed. NHibernate tracks objects so it can know if any changes need writing to the database.
A call to Update
is used to tell NHibernate to start tracking an object that it is not being tracked already (aka detached object). In the example above, the Post is loaded from the NHibernate session, and is already being tracked. Calling Update
on an object that is already being tracked is silly.
It's easy to think that Update
signals NHibernate to save changes to the database - to instantly flush them. This is not the case. Update
simply indicates an object has changes to be saved, not when they are saved. Don't use Update
in an attempt to trigger an instant flush, that's what Flush
and Commit
are for.
Your code will look cleaner without Update
s all over the place. Consider this: our latest NHibernate driven MVC app makes zero calls to Update
. We don't need Update because we don't have a scenario where we detach and attach objects to the NH session. All functionality can be achieved though Save
, Commit
and Rollback
.
The previous example should really just exclude the redundant Update
as follows:
public ActionResult UpdatePost(int id, FormCollection form)
{
var post = _nhibernateSession.Load<Post>(id);
post.Title = form["PostTitle"];
post.Body = form["PostBody"];
_nhibernateSession.Transaction.Commit();
}
When Update Is Required
So when is an Update
required? Here's a valid use of Update:
public ActionResult UpdatePost(int id, FormCollection form)
{
var post = HttpContext.Session[id] as Post;
post.Title = form["PostTitle"];
post.Body = form["PostBody"];
_nhibernateSession.Update(post);
_nhibernateSession.Transaction.Commit();
}
Here, the Post
is pulled out of the Http session -- it is not being tracked yet. Then, it's properties are changed, and Update
is called. The object is re-associated with the session, and NHibernate will assume that it needs saving even though it observed no changes after re-association. In other words, Update
implies a save is required (Lock
can be used to associate an object with the session without making NHibernate assume a save is required).
The call to Update
is valid when a detached object is being re-associated with a session, and you want the session to assume the object has changed.
This variation will also work:
public ActionResult UpdatePost(int id, FormCollection form)
{
var post = HttpContext.Session[id] as Post;
_nhibernateSession.Update(post); // <--
post.Title = form["PostTitle"];
post.Body = form["PostBody"];
_nhibernateSession.Transaction.Commit();
}
The end result is the same, but because Update
is called before the changes are applied to the object, NHibernate will waste CPU cycles tracking the changes made after the call to Update
before finally flushing them to the database. It's not bad, just a tad wasteful.