For some time I have put off implementing lazy-loading in my current project because (ironically) I am lazy. It is just conceptually easier to load the object you want and then move on. I think this due to a brief encounter I had with a NHibernate-created frankenproxy when I issued a 'ToString()' on one of the objects I had loaded and as far as I recollect I got the crazy proxy name instead of the override ToString() value I expected. Now I am sure now I had simply not overriden ToString() like I thought I had but I simply haven't had time to sit down and design as if my objects were lazy loaded.The notes here are what I picked up today so if you see something wrong please let me know!
The proxied instances returned by NHibernate will invoke the ToString(), Equals(), and GetHashCode() methods to the target object type if they have been overriden (which I hope is the case). This might be a concern since the proxies themselves would have these methods, but they are transparent in this regard.
The newer versions of NHibernate load lazily by default, so it is important to have made decisions and understand what your Unit of Work strategy is. Assuming this is an ASP.NET application, are you storing your ISession in the Context.Items and having the end of the Request flush your ISession? ( a common practice) This is relevant because it is important to know that if you are working with objects that have been loaded lazily after the session has been flushed will throw an NHibernate exception since the proxy can't invoke to its target without an ISession. If this comes up, you may reattach the object to the current ISession using session.Lock(myObject) . Personally, I use FlushMode.Commit mode since I like things to happen when I instruct them to and it is conceptually easier for me than to tie my .Flush() to an event.
There are a couple of options for determining the objects NHibernate will return from its lazy-loads.
The most common is to simply mark the class with the 'lazy="true"' attribute or place 'default-lazy="true"' in the mapping declaration:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"assembly="@core.assembly@"
default-access="nosetter.camelcase-underscore" default-lazy="true">
Or
<class name="Cei.eMerge.Core.Domain.Contacts.Contact" table="Contact"lazy="true" >
If you go this route, remember the following:
The other option is to tell NHibernate what interface to return for your proxy using the 'proxy="IMyInterface"' in the class declaration:
<class name="Cei.eMerge.Core.Domain.Contacts.Contact" table="Contact"proxy="IContact" >
Remember this is you go this route:
Properties and methods don't need to be marked virtual.
/// <summary>
/// The First Text of an Individual Contact. Such as "Franz" or "Harry".
/// </summary>
public virtual string FirstName
{
get
{
return _firstName;
}
protected set{ _firstName = value;}
}
The benefits of lazy-loading can be immense. Not only can you avoid the common select n+1 problem, but you can drastically reduce the size of the queries hitting your db. This will especially be important if you have, say, turned ViewState off and are hitting the DB for data with each request and caching isn't an option.
UPDATE: I goofed when I wrote this (late at night).Select N+1 is something to beware of when dealing with lazy loading. See this post by Oren Eini for ways to deal with this. Thanks Bill for catching my error!