UPDATE, reflecting the query changes
The test you've showed in the query update, has proven:
If the
parent.Children
is set tonull
and persited, it will have NO children - next time accessed.
Let me explain what happened, let me use some virtual language (Attention I am using Parent
and Children
to make it simple)
1) mapping of the parent child is cascade="save-update"
This is an information for NHibernate that during creation or amending, children collection should be passed the Save() or Update() calls
2) Let's load parent
var session = ... // get a ISession for our test
var parent = session.Get<Parent>(1); // e.g. DocumentFieldDTO
// NOT Empty -- is true: IsNotEmpty()
Assert.IsTrue(parent.Children.IsNotEmpty()); // e.g. DocumentFieldOrgs
3) Now, remove the reference and check what NHibernate will do:
parent.Children = null;
session.Flush();
session.Clear();
Here is the SQL statement executed:
exec sp_executesql N'UPDATE [schema].[Child_Table]
SET ParentId = null WHERE ParentId = @p0',N'@p0 int',@p0=1
As we can see, because of the mapping save-update
, NHibernate did handle this scenario, by removing the reference. In fact by UPDATing the child table
4) Load Parent
again
parent = session.Get<Parent>(1);
// EMPTY -- is true: IsEmpty()
Assert.IsTrue(parent.Children.IsEmpty());
Summary: As we've seen above, NHibernate is doing what was mapped, and expected. No delete. Just an update, removing the reference
PREVIOUS part of this Answer
The answer is: change your public int? Save(...)
implementation. NHibernate cascading is working as expected, please read more here Ayende, NHibernate Cascades: the different between all, all-delete-orphans and save-update
First take a look at the statement above:
DESIRED BEHAVIOR:
1) Cascade any changes to the collection (whether in the parent was retrieved by NHibernate or not).
2) Do not delete objects even if the parent does not have a collection of children.
The bold parts are the reason, why the Cascade concept
is not working. Because:
Cascade makes sense only if an operation on
the existing parent is cascaded / repeated / passed
the existing/known child(children)
NHiberante cascade implementation realy does work this way: 9.9. Lifecyles and object graphs (an extract)
Mapping ... with
cascade="all"
marks the association as a parent/child style relationship where save/update/deletion of the parent results in save/update/deletion of the child(ren). ... A child which becomes unreferenced by its parent is not automatically deleted, except in the case of a<one-to-many>
association mapped with cascade="all-delete-orphan"...
Not only it is not deleted. If it is not referenced, it does not recieve a trigger to any type of cascading operation.
Suggestion:
Adjust the Save()
method, to do two operations:
- Update the parent
- Find the "children" or better - the somehow related items. Load them adjust them, and call
session.Flush()
. Any changes on objects referenced by ISession will be persisted.