— New online course!  Getting Started with Azure DevOps — Special launch price 50% off —

Custom Lazy-loadable Business Collections with NHibernate

by

I was at VSLive Las Vegas last week and one of my talks was on NHibernate development.  One of the guys who was in my session came up to me and asked “how do I create my own custom collection that I can use in NHibernate that still lets me do lazy loading?” 

The core of the problem is that NHibernate’s lazy-loading prefers to do interfaces for public colleciton properties.  At runtime, NHibernate sets in a proxy that implements the appropriate collection interface and your code never knows the difference.  It’s that proxy class that you get at runtime that implements the lazy loading logic — if you access a property on that collection and the data hasn’t been loaded yet, the proxy goes out and gets it before returning the appropriate value(s). 

That’s great until you need to do some custom work as part of one of the collection methods or until you need a new method.  The only way you can do that is to plug in a custom collection — but then you lose all ability to do lazy loading. 

To get around this problem, I created an abstract class in my Com.Benday.NHibernate library called LazyLoadableBusinessCollection<T>.  When you need a custom collection, extend from this class and you’ll still be able to do lazy loading.  (BTW, I came up with this class in probably January of 2007 and it’s taken me this long to get around to actually blogging about it.)

How does it work and how do you use it?:

1. Lazy loading happens via the Elements property on LazyLoadableBusinessCollection. 

private IList<T> m_elements;
protected IList<T> Elements
{
    get
    {
        if (m_elements == null) m_elements = new List<T>();

        return m_elements;
    }
    set
    {
        m_elements = value;
    }
}

2. Instead of mapping the <bag> and letting NHibernate create your collection, use <component> to create an instance of your custom collection, and map the <bag> to LazyLoadableBusinessCollection’s Elements property

<class name=”Person”>
    <id name=”Id” unsaved-value=”0″>
        <generator class=”native” />
    </id>
    <property name=”FirstName” not-null=”true” />
    <property name=”LastName” not-null=”true” />
    <property name=”PhoneNumber” not-null=”true” />
    <property name=”EmailAddress” not-null=”true” />
    <property name=”Status” not-null=”true” />
    <!–
    MAPPED WITH IList<Restaurant>
    <bag name=”FavoriteRestaurants” generic=”true”
         lazy=”true” cascade=”all”
             table=”PersonRestaurant”>
         <key column=”PersonId”></key>
         <many-to-many column=”RestaurantId”
        class=”Restaurant”></many-to-many>
    </bag>
    –>

    <component
        name=”FavoriteRestaurants”
        class=”RestaurantCollection”>
        <bag name=”Elements” lazy=”true”
            cascade=”all-delete-orphan” generic=”true”
            inverse=”true” table=”PersonRestaurant”>
            <key column=”PersonId”></key>
            <many-to-many column=”RestaurantId”
            class=”Restaurant”></many-to-many>
        </bag>
    </component>
</class>

3. All the IList<T> features still work because LazyLoadableBusinessCollection forwards all IList<T>, ICollection<T>, IEnumerable<T>, and IEnumerable properties and methods to the appropriate method/property on the Elements property. 

#region IList<T> Members

public virtual int IndexOf(T item)
{
    return this.Elements.IndexOf(item);
}

public virtual void Insert(int index, T item)
{
    this.Elements.Insert(index, item);
}

public virtual void RemoveAt(int index)
{
    this.Elements.RemoveAt(index);
}

public virtual T this[int index]{
    get
    {
        return this.Elements[index];
    }
    set
    {
        this.Elements[index] = value;
    }
}

#endregion

Here’s a link to download a sample application that demonstrates the LazyLoadableBusinessCollection<T>.  Check out the following files: RestaurantCollection.cs, Person.cs, Person.hbm.xml, and LazyLoadableBusinessCollection.cs. 

Let me know if you have any questions.

-Ben


One Response to "Custom Lazy-loadable Business Collections with NHibernate"

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: