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
if (m_elements == null) m_elements = new List<T>();
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
<id name=”Id” unsaved-value=”0″>
<generator class=”native” />
<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”
<bag name=”Elements” lazy=”true”
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)
public virtual void Insert(int index, T item)
public virtual void RemoveAt(int index)
public virtual T this[int index]
this.Elements[index] = value;
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.