I introduced a mapping for a business object which has (among others) a property called “Name”:
public class Foo : BusinessObjectBase
{
...
public virtual string Name { get; set; }
}
For some reason, when I fetch “Foo” objects, NHibernate seems to apply lazy property loading (for simple properties, not associations):
The following code piece generates n+1 SQL statements, whereof the first only fetches the ids, and the remaining n fetch the Name for each record:
ISession session = ...IQuery query = session.CreateQuery(queryString);
ITransaction tx = session.BeginTransaction();
List<Foo> result = new List<Foo>();
foreach (Foo foo in query.Enumerable())
{
result.Add(foo);
}
tx.Commit();
session.Close();
produces:
select foo0_.FOO_ID as col_0_0_ from V1_FOO foo0_
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_
WHERE foo0_.FOO_ID=:p0;:p0 = 81
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_
WHERE foo0_.FOO_ID=:p0;:p0 = 36470
SELECT foo0_.FOO_ID as FOO1_2_0_, foo0_.NAME as NAME2_0_ FROM V1_FOO foo0_
WHERE foo0_.FOO_ID=:p0;:p0 = 36473
Similarly, the following code leads to a LazyLoadingException after session is closed:
ISession session = ...
ITransaction tx = session.BeginTransaction();
Foo result = session.Load<Foo>(id);
tx.Commit();
session.Close();
Console.WriteLine(result.Name);
Following this post, “lazy properties … is rarely an important feature to enable … (and) in Hibernate 3, is disabled by default.”
So what am I doing wrong? I managed to work around the LazyLoadingException by doing a NHibernateUtil.Initialize(foo) but the even worse part are the n+1 sql statements which bring my application to its knees.
This is how the mapping looks like:
<class name="Foo" table="V1_FOO">
...
<property name="Name" column="NAME"/>
</class>
BTW: The abstract “BusinessObjectBase” base class encapsulates the ID property which serves as the internal identifier.
I don’t think that this is due to lazy property loading. It’s rather because of the use of
EnumerableandLoad.Take a look at the reference documentation about
Enumerable:Either use batch fetching to reduce the number of queries (in the mapping of the class)
… or use
Listinstead of Enumerable:Note:
Enumerableonly makes sense if you don’t expect that you need the whole result, or in special cases where you don’t want to have them all in memory at the same time (then you needEvictto remove them). For the most cases,Listis what you need.In the case of
Load, only a proxy is created (no query is performed). On the first access to it, it is loaded. (This is very powerful for instance to use this proxy as filter arguments in queries or to link it to another entity without the need of loading its contents.) If you need its contents, useGetinstead… or even better, use the entity only while the session is open.