I have an odd problem in my current project. Lazy loading for queries does not work. When I query a list, nhibernate fetches all associations separately.
I extracted small parts of it and put it into a separate solution. Basically what I've got now, is a Account-Table and a AccountSync-Table. Both have an ID and a URL, while the ID is just a db-guid.
My classes are:
public class HippoAccount
{
public virtual Guid Id { get; set; }
public virtual string Url { get; set; }
public virtual HippoAccountSync Sync { get; set; }
}
public class HippoAccountSync
{
public virtual Guid Id { get; set; }
public virtual string Url { get; set; }
public virtual HippoAccount Account { get; set; }
}
When I now load a object via it's guid:
var account = session.Load<HippoAccount>(accountId);
Console.WriteLine(NHibernateUtil.IsPropertyInitialized(account, "Sync"))
... it returns false
and account itself is a proxy.
But when loading a list via the criteria API:
var account = (HippoAccount)session
.CreateCriteria(typeof (HippoAccount))
.Add(Restrictions.Eq("Id", accountId))
.List()[0];
... the property Sync
gets initialized (firing a second select query), and the returned object is not a proxy.
Is that default behaviour? What am I getting wrong?
The mapping is:
<class name="HippoAccount" table="AllAccounts">
<id name="Id" type="guid">
<generator class="guid"/>
</id>
<property name="Url" />
<many-to-one
class="HippoAccountSync"
name="Sync"
not-found="ignore"
property-ref="Url">
<column name="url" />
</many-to-one>
</class>
<class name="HippoAccountSync"
mutable="false"
table="Accounts">
<id name="Id" type="guid">
<generator class="guid"/>
</id>
<property name="Url">
<column name="serviceUri" />
</property>
<many-to-one class="HippoAccount"
name="Account"
property-ref="Url"
not-found="ignore">
<column name="serviceUri" />
</many-to-one>
</class>
After quite some more research, I found the answers. Answers, because there are many things that can prevent lazy loading in NHibernate.
Query vs. session.Load: When fetching an item via session.Load()
you get a proxy. But as soon as you access any property, lets say the Url
, the object is fetched including all it's associations that doesn't support lazy loading.
property-ref: Lazy loading only works over a objects id. When an property-association is resolved via a different column in the target entity, NH fetches it eagerly. Not that this wouldn't be possible, it's just not implemented: Bug
not-found="ignore" allows invalid foreign keys, that is, if the referenced entity isn't found NH will init the property with null. NH doesn't intercept the property-access for lazy loading, but instead assignes a object proxy. With not-found="ignore"
it can't decide if the property should be set to null or a proxy for the given, possibly invalid, foreign key. This could possibly be solved by intercepting the property access.
When disabling not-found="ignore"
and property-ref
the schema export would generate constraints that enforce a circular reference. Not good! The correct mapping would then be a constrained one-to-one relationship, where the key for HippoAccountSync
must have a generator foreign
.
Resources
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With