Assigning Foreign Keys In NHibernate
Posted by Davy Brion on April 3rd, 2009
This post from the Entity Framework team recently caught my attention. It discusses the ability to add actual foreign key values to your entities instead of just references to the referred entities. One of the benefits of this ability is that you can assign foreign key values to an entity’s properties without having to actually retrieve the entity you are referring to. While i am no fan of this approach, i do want to point out that you can do this with NHibernate too, especially because some people don’t know about this.
Take a look at the following code:
product.Category = session.Get<ProductCategory>(categoryId);
session.SaveOrUpdate(product);
This code changes the product’s Category property, and to do that it retrieves the actual ProductCategory instance through the id value of the category. This causes 2 database hits. One to retrieve the ProductCategory, and one to persist the Product.
You could do this instead:
product.Category = session.Load<ProductCategory>(categoryId);
// this verifies that the product.Category is an uninitialized proxy
// which means that we did not fetch the product category from the database
Assert.IsFalse(NHibernateUtil.IsInitialized(product.Category));
// we were able to save the product without having loaded the product category
session.Save(product);
Notice how we use ISession’s Load method here, instead of the Get method to ‘retrieve’ the ProductCategory. The Get method actually fetches the entity from the database if it’s not already in the session cache. The Load method however will return an uninitialized proxy to the ProductCategory entity if it’s not present in the session cache. The NHibernateUtil.IsInitialized() method will return false, because this proxy is indeed uninitialized. It does not hit the database until you try to access any of the properties of the ProductCategory proxy, except for its identifier property. So accessing product.Category.Id would not hit the database, but product.Category.Name or product.Category.Description would.
If you want to avoid hitting the database to assign foreign keys, using a proxy might be an interesting alternative for you.

April 5th, 2009 at 6:06 pm
Nice!
Any idea on how to accomplish this with Castle ActiveRecord?
April 5th, 2009 at 6:13 pm
@Robert
no idea actually… never used Castle ActiveRecord
April 6th, 2009 at 3:17 am
Nice, I’d wondered about doing something like this for handling selections from a combo/drop-down.
Out of curiosity, why don’t you like this approach? Would you prefer to cache the referenced object(s)?
April 6th, 2009 at 6:03 am
@David
if the record whose Id you’re using no longer exists, i prefer to throw an exception with a meaningful message. If you use the proxy approach to avoid the database hit, you’d get a relatively obscure exception message if the foreign key value turns out to be invalid.
as for the caching… i try to do that only where it really makes sense, never as a general approach
April 9th, 2009 at 12:29 pm
Fair enough. I guess the use cases I’m thinking of involve relatively static data, but I agree with your reservations. Still, thanks for sharing the approach!
October 19th, 2009 at 9:11 pm
Why in below example product’s Category won’t be updated?? Checked in NHibernate 2.1. For me it’s a bug.
Product product = session.Get(productId);
product.Category = session.Load<ProductCategory>(categoryId); (// or session.Get…
session.Update(product);
When I use session.Flush(), product’s Category will be updated.
October 19th, 2009 at 9:26 pm
NHibernate tries to defer sending statements to the database as long as possible. If possible, it will wait until the session is flushed or the transaction is committed (depending on the FlushMode setting).
The Update method itself doesn’t really mean “send an update statement for this instance to the database”. It’s basically a way of attaching a detached entity to the current ISession (or, registering an unregistered instance in the current Unit Of Work if you prefer those terms). In fact, NHibernate will try to update the instance of any persistent object if it has been changed once the session is flushed (or the transaction is committed) even if you don’t call the Update method. Since the product instance was retrieved through the session, it is already a persistent object and NHibernate will automatically persist any changes to that instance to the database.
I think you should read up on how NHibernate’s Unit Of Work implementation (basically your session) works, and how it deals with persistence of entities based on their state (transient, persistent or detached).
October 19th, 2009 at 10:45 pm
Thanks for answer. I won’t ask any more questions before ending up reading of NHibernate Reference Guide.
This Update method mislead me because of the Update method in your series about writing custom DAL. BTW. Those posts saved me from writing more hand coded “Active Record” entities for my SharePoint project, now I have my own SharePointHibernate:)
December 15th, 2009 at 9:11 am
Thanks Davy, this solved a lot of unneeded queries!