Wednesday, December 19, 2012

NHibernate Inverse and Cascade

Best explanation: http://www.emadashi.com/2008/08/nhibernate-inverse-attribute/

Suppose we have a Parent with a collection of Child objects (one to many)

public class Parent

{

public virtual int Id { get; set; }

public virtual string Name { get; set; }

public virtual IList<Child> MyChildren { get; set; }

}

public class Child

{

public virtual int Id { get; set; }

public virtual string Name { get; set; }

public virtual Parent MyParent { get; set; }

}

 

Parent par = Session.Get<Parent>(8);

Child ch = new Child();

ch.Name = “Emad”;

par.MyChildren.Add(ch);

Session.Save(par);

 

First, we need to set the cascade on the MyChildren, such that when ever you save the Parent, all the Child objects in the MyChildren collection are forced to be saved as well.

Also, by default, the parent object will also set the ParentId column on the newly inserted Child, because there is a hidden property on the MyChildren collection which is by default false. This means the Parent object will manage the relationship by updating the ParentId foreign key on the child row.

So what will happen is:

1. the new child is added

2. the new child ParentId foreign key is updated

The problem comes when you put the null constraint on the MyParent, the insert will fail because the ParentId is not yet set.

To overcome the issue, the solution is to:

1. set inverse attribute to false for the MyChildren collection. this will not update the ParentId foreign key anymore on the Child

2. explicitly set the Parent property in code for the Child object

Parent par = Session.Get<Parent>(8);

Child ch = new Child();

ch.Name = “Emad”;

ch.MyParent = par;

par.MyChildren.Add(ch);

Session.Save(par);

SaveOrUpdate Vs Update and Save in NHibernate

Chapter 9 :

http://www.nhforge.org/doc/nh/en/index.html

But cliff notes:

Save() takes a new object without an identifier and attaches it to the session. The object will beINSERT'd.

Update() takes an existing object that has an identifier but is not in the session and attaches it to the session. The object will be UPDATE'd.

SaveOrUpdate() looks at the identifier and decides what is necessary in the above.

SaveOrUpdateCopy() is special in that say you have two objects with the same identifier -- one in the session and one not. If you try and update the one not in the session an exception is thrown normally (you are now trying to attach two objects that represent the same persistent object to the session).SaveOrUpdateCopy() copies the non-session object state to the session object state.

I'm not sure how you are going to use NH, but for a lot of cases all you need is Save(). The session is doing ALL of the work necessary to know what has to be updated and simply Flush() or a Commit()does everything you need.

You usually don't need SaveOrUpdate() because NHibernate tracks changes to every loaded object. To update an object use Session.Get(), make you change then call Session.Flush()

NHibernate defaults

Nullable – yes, all properties are nullable by default
Lazy – yes, all properties are lazy by default. They must be virtual and public or protected.
Cascade – default is None
Inverse - default is false, meaning this side is maintaining the relationship

NHibernate Get vs Load behavior in whether returning the proxy or not

When returning an entity by using Get or Load, NHibernate first tries to return it from the session cache.
If it’s not there, then Get and Load will return the entity in a different way.
When calling Get, NHibernate will perform a roundtrip to database and return the non-proxy entity object or null in case the record does not exist:
var user = session.Get<User>(122);
For Load, NHibernate will return a proxy of the entity object or throw an exception if the record does not exist:
var user = session.Load<User>(122);
Suppose we have the following calls:
var a = session.Load<Assignment>(1);
var b = session.Get<Assignment>(1);
Note that in the 2nd case, b is a proxy and not the entity because the entity proxy exists in the session cache due to the Load call.
var a = session.Get<Assignment>(1);
var b = session.Load<Assignment>(1);
In this case b is a non-proxy entity object, due to the Get call.