NHibernate creates proxy via session.Load(), but not via Linq or Criteria API - nhibernate

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
Select statement issued for each not-found=ignore
Lazy-load conflicts with Property-ref in Many-to-One Mapping
Google groups discussion

Related

NHibernate Many-to-one cascade

I have the below two classes:
public class Project
{
public virtual int ProjectId { get; set; }
public virtual string ProjectName { get; set; }
public virtual LegalEntity LegalEntity { get; set; }
}
and
public class LegalEntity
{
public virtual int LegalEntId { get; set; }
public virtual string Name { get; set; }
}
with mappings as:
<class name="Project" table="Project" dynamic-update="true">
<id name="ProjectId">
<generator class="native"/>
</id>
<property name="ProjectName" />
<many-to-one name="LegalEntity" column="LegalEntId" fetch="join" cascade="all-delete-orphan" />
</class>
and
<class name="LegalEntity" table="LegalEnt" dynamic-update="true">
<id name="LegalEntId">
<generator class="native"/>
</id>
<property name="Name" />
</class>
In database, Project table has a FK to LegalEntity's PK column. One Project will have only one legal entity. Different projects can have same legal entity. So thats the reason I have gone for many-to-one. Not sure if this is correct though.
Insert and update is working fine. But if I update a legal entity id in a project and that legal entity becomes orphan, I want it to be deleted. But its not happening. Am I wrong in understanding delete-all-orphan? If yes, how can I achieve this behaviour?
The many-to-one cascade does not support all-delete-orphan, see:
5.1.10. many-to-one
<many-to-one
...
cascade="all|none|save-update|delete" (4)
...
Also, it would be almost impossible to handle this feature by NHibernate's session. Because it does not have to be clear, that the referenced many-to-one is really orphan. There should be some farther checks in DB... there could be other places referencing this table row...
Suggestion: do it in your code as a part of the DAO or Business Facade implementation. Check if there are really no dependencies, and then issue explicit Delete()
EXTEND: Here is a QueryOver syntax to get a list of all "orphan" LegalEntity
// subquery
var subquery = QueryOver.Of<Project>()
.Select(x => x.LegalEntity.LegalEntId);
// just these legal entities, which are NOT used
var query = session.QueryOver<LegalEntity>()
.WithSubquery
.WhereProperty(y => y.LegalEntId)
.NotIn(subquery)
;
// orphans
var list = query
.List<LegalEntity>();
Now all-delete-orphan and delete-orphan have been implemented for many-to-one as you can see in this commit from Nov 19, 2014.
Those were not supported when the OP asked the questions or when Radim Köhler wrote his answer, but I think future visitors will appretiate the update.
The documentation is also updated and now says:
cascade="all|none|save-update|delete|delete-orphan|all-delete-orphan"
But the documentation is confusing now, because it still has the following note:
The cascade attribute permits the following values: all, save-update, delete, none.
So I've created a defect to fix that last part of the documentation.

Automatically removing associations when deleting entities in NHibernate

I have the following entities:
namespace NhLists {
public class Lesson {
public virtual int Id { get; set; }
public virtual string Title { get; set; }
}
public class Module {
public virtual int Id { get; set; }
public virtual IList<Lesson> Lessons { get; set; }
public Module() {
Lessons = new List<Lesson>();
}
}
}
And the following mappings:
<class name="Module" table="Modules">
<id name="Id">
<generator class="identity"/>
</id>
<list name="Lessons" table="ModuleToLesson"
cascade="save-update">
<key column="moduleId"/>
<index column="position"/>
<many-to-many
column="lessonId"
class="NhLists.Lesson, NhLists"/>
</list>
</class>
<class name="Lesson" table="Lessons">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Title">
<column name="Title" length="16" not-null="true" />
</property>
</class>
When I delete a lesson by session.Delete(lesson), is there anyway I can have NHibernate automatically update the association in Module.Lessons to remove the entry from the set? Or am I forced to go through all Modules and look for the lesson and remove that by hand?
Edit: Fixed ICollection and <set> in mappings to IList<> and <list> like I want and tested it.
You have false idea. If you want to delete the Lesson object from Module you do that manually. NHibernate just tracks such your action and when session.Commit() is called then the reference between Module and Lesson is deleted in the database.
Calling session.Delete(lesson) deletes the lesson object from database (if foreign keys are set properly then reference between Module and Lesson is deleted of course but it is not responsibility for NHibernate).
In conclusion, it is not possible to delete the lesson object from the Module.Lessons list automatically by calling session.Delete(lesson). NHibernate does not track such entity references.
Turns out that if we do not need IList semantics and can make do with ICollection the update problem can be solved by adding a reference back from Lesson to Module, such as:
public class Lesson {
...
protected virtual ICollection<Module> InModules { get; set; }
...
}
And to the mapping files add:
<class name="Lesson" table="Lessons">
...
<set name="InModules" table="ModuleToLesson">
<key column="lessonId"/>
<many-to-many column="moduleId" class="NhLists.Module, NhLists"/>
</set>
</class>
Then a Lesson deleted is also removed from the collection in Module automatically. This also works for lists but the list index is not properly updated and causes "holes" in the list.

Not loading associations without proxies in NHibernate

I don't like the idea of proxy and lazy loading. I don't need that. I want pure POCO. And I want to control loading associations explicitly when I need.
Here is entity
public class Post
{
public long Id { get; set; }
public long OwnerId { get; set; }
public string Content { get; set; }
public User Owner { get; set; }
}
and mapping
<class name="Post">
<id name="Id" />
<property name="OwnerId" />
<property name="Content" />
<many-to-one name="Owner" column="OwnerId" />
</class>
However if I specify lazy="false" in the mapping, Owner is always eagerly fetched.
I can't remove many-to-one mapping because that also disables explicit loading or a query like
from x in session.Query<Post>()
where x.Owner.Title == "hello"
select x;
I specified lazy="true" and set use_proxy_validator property to false. But that also eager loads Owner.
Is there any way to load only Post entity?
In short, it is not possible with out of box NH. But here is attempt at just, lazy loading without proxies
http://thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance
Set the class User to lazy = false on the mapping
<class name="User" table="Users" lazy="false">
Remove this property <property name="OwnerId" />... to get the owner id you can use Owner.Id. This will not trigger a lazy load. Owner will only be loaded if you hit any property besides the id. To make it a flat/simple POCO, you can use projections and ResultTransformers.
Davy Brion - Must Everything be Virtual with NHibernate

NHibernate one-to-many relationship lazy loading when already loaded

I have a tree where every node is a Resource class:
public abstract class Resource
{
public virtual Guid Id { get; set; }
public virtual Resource Parent { get; set; }
public virtual IList<Resource> ChildResources { get; set; }
}
as you can see this class is abstract and there are many different derived classes from Resource (3 at the moment, more to come).
In my database i have a table for Resource, and a table for each class which
derives from Resource. These are mapped together with <joined-subclass>.
I've read this:
http://ayende.com/Blog/archive/2009/08/28/nhibernate-tips-amp-tricks-efficiently-selecting-a-tree.aspx
and i have the same code as Ayende to load my tree:
var resource = UnitOfWork.Current.Session
.CreateQuery("from Resource r join fetch r.ChildResources")
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.SetReadOnly(true)
.List<Resource>();
which is all working fine (all Resources are returned with a single select) However, I'm seeing extra selects occurring as I enumerate a Resource's ChildResources list.
Is that because of this?:
http://ayende.com/Blog/archive/2009/09/03/answer-the-lazy-loaded-inheritance-many-to-one-association-orm.aspx
Either way, how do I prevent this from happening?
Here's the part of the mappings for the relationships (class names
trimmed for clarity):
<bag cascade="save-update" fetch="join" lazy="false" inverse="true" name="ChildResources">
<key>
<column name="Parent_Id" />
</key>
<one-to-many class="Resource" />
</bag>
<many-to-one class="Resource" name="Parent">
<column name="Parent_Id" />
</many-to-one>
Thanks
UPDATE
Slight oversight, its only issuing extra selects when enumerating the child collections of the leaf nodes in the tree...
Either do this:
<bag ... lazy="false">
to eager fetch the items always, or do this (in HQL):
var resources = session.CreateQuery("from Resource r join fetch r.ChildResources");

Lazy loading not working for many-to-one relationship when mapping to a non-key field using property-ref

I have a legacy database that I am mapping using NHibernate. The objects of concern are an Account and a list of Notification objects. The objects look like:
public class Notification
{
public virtual int Id { get; set; }
public virtual DateTime BatchDate { get; set; }
/* other properties */
public virtual Account Account { get; set; }
}
public class Account
{
public virtual int Id { get; set; }
public virtual string AccountNumber { get; set; }
/* other properties */
}
The mapping files look like:
<class name="Account" table="Account" dynamic-update="true">
<id name="Id" column="AccountID">
<generator class="native" />
</id>
<property name="AccountNumber" length="15" not-null="true" />
<!-- other properties -->
</class>
<class name="Notification" table="Notification">
<id name="Id" column="Id">
<generator class="native" />
</id>
<!-- other properties -->
<many-to-one name="Account" class="Account" property-ref="AccountNumber" lazy="proxy">
<column name="AcctNum" />
</many-to-one>
However, when I create a criteria such as
return session.CreateCriteria(typeof(Notification)).List<Notification>();
I am getting a Select N+1 case where each account is loaded even though the Account is never referenced. Why are all of the accounts getting loaded when the many-to-one is mapped as a lazy proxy?
The issue is caused by the property-ref attribute. Lazy loading only works when the many-to-one reference is using the other object's primary key since NHibernate assumes there's a foreign key constraint enforcing the validity of such a value. With a non-primary key (indicated by the property-ref), NHibernate does not make this assumption and thus does not assume the related object must exist. Since it does not want to create a proxy for an object that does not exist (i.e. should be null instead of a proxy), it eagerly fetches the remote object. This same issue exists when not-found="ignore" is specified since this indicates that the foreign key relationship is not enforced and may result in a null reference.
See also:
NHibernate creates proxy via session.Load(), but not via Linq or Criteria API
http://frankmao.com/2007/12/05/lazy-load-conflicts-with-property-ref-in-many-to-one-mapping/