4
votes

I have seen this problem a lot, but none of the solutions have worked for me.

I have a one-to-many relationship in C# mapped to MS SQL with fluent NHibernate. When I try to delete a child element, NHibernate tries to do so by setting the foreign key to NULL which of course throws an error.

The solutions to similar questions have been to add Inverse to the HasMany mapping of the parent. However now there is this problem:

var parent = //something
parent.Children.Clear();
session.Update(parent);

This causes the entire parent to be deleted! Why?

1

1 Answers

11
votes

The use of "Inverse" in a mapping reverses the concept of who "owns" the relationship between parent and child. By specifying "Inverse", NH basically acts as if the Child side of the relationship decides whether it belongs to a Parent, instead of the Parent deciding whether it has the Child. A good real-world example is Students being enrolled in a College. A Student can choose not to belong to a College anymore, and can still exist and be meaningful as an entity. It's also the Student, not the College, who is the primary decider of whether to form or sever this relationship to a College (in real life, yes, there are situations in which the college says that the student is no longer welcome, but the college doesn't just tell a student in good standing "you're dropping out"; it's the student who tells the college that).

The short of it is that by specifying the relationship between Parent and Children as Inverse, NH is treating the "one" side of the relationship (Parent) as the side that cannot exist outside the context of the relationship. So, when you clear all the Children, the Parent, which now has no Children, is "orphaned" and NH deletes it.

This does not sound like what you want; so, I would remove the Inverse mapping from this relationship, allowing the Parent to "own" this relationship with its Children. Remove all Children and they become orphaned and deleted, but the Parent is still around. Now, you have the problem that the Children cannot be "orphaned" because the foreign key is not nullable; they have to belong to something. NH requires the FK of child records to be nullable if you're going to use "orphan deletion" cascading rules. This is because orphan deletion is a two-pass operation that requires the record to first be orphaned, by setting its FK field to null. NH will then run a second statement to delete any record with a NULL FK from the table. So, even with a nullable FK field, by using NH with the desired cascade rules, you won't have any records with a null FK for more than a transient period.

If this is unacceptable, you'll have to remove orphan deletion from the cascading rules, and manually delete each record as well as remove it from the Session.

foreach(var child in parent.Children)
      session.Delete(child);

   parent.Children.Clear();
   session.Update(parent);