The Inquisitive Coder – Davy Brion's Blog

Trying to walk that thin line between intelligence and ignorance

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.

9 Responses to “Assigning Foreign Keys In NHibernate”

  1. robert Says:

    Nice!

    Any idea on how to accomplish this with Castle ActiveRecord?

  2. Davy Brion Says:

    @Robert

    no idea actually… never used Castle ActiveRecord

  3. David Says:

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

  4. Davy Brion Says:

    @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

  5. David Says:

    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!

  6. Whut Says:

    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.

  7. Davy Brion Says:

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

  8. Whut Says:

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

  9. Geert V Says:

    Thanks Davy, this solved a lot of unneeded queries!

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>