You are getting the first error because, when you remove the items from the collection, NHibernate's default mode of operation is to simply break the association. In the database, NHibernate tries to set the foreign key column on the child row to null. Since you do not allow nulls in that column, SQL Server raises the error. Clearing the collection will not necessarily delete the child object, but one way to do so is to set cascade=all-delete-orphan. This informs NHibernate that it should delete the newly orphaned rows instead of setting the foreign key column.
You are getting the second error because when you call SaveOrUpdate NHibernate first deletes all of the child objects. Then, because neither relationship is marked as inverse, NHibernate also tries to set the foreign key column in your child table to null. Since the rows have already been deleted, you receive the second error. You need to set inverse=true on one side of your relationship to fix this. This is usually done on the one (primary key or parent) side. If you do not do this, NHibernate will make the appropriate updates for each side of the relationship. Unfortunately, running two updates is not the appropriate thing to do.
You should always mark one side of your relationships as the inverse side. Depending on how you code, you may or may not need to use cascading. If you want to take advantage of one shot deletes as you are trying to do using Clear(), you need to define your cascade.
If it is a many-to-many, then one direction is the "master" direction. The other direction will have inverse="false"
defined. You have to remove from the collection the right way round - to be sure (and to truly represent what your deletion is achieving), delete both. Then flush the session to have this persisted.
Example:
if A
has a collection of objects of type B
, and vice versa, then to remove the association between two particular instances, you have to delete the instance of A
from the instance of B
's collection and vice versa. Then when the session is flushed, NHibernate knows to delete the relevant row from the linking table.
Edit now you've posted code
Try changing the two methods like so:
public virtual void RemoveTeamFromEmployee(Team team)
{
Teams.Remove(team);
team.Employees.Remove(this);
}
public virtual void RemoveEmployeeFromTeam (Employee employee)
{
Employees.Remove(employee);
employee.Teams.Remove(this);
}
To tidy up your code to delete a team, which is not responsible for the relationship, you do have to iterate over the linked employees. However, if you have to delete teams more frequently than employees, then you may find it more convenient to reverse this, and then you can just use:
team.Employees.Clear();
session.Delete(team);
Best Answer
This is from section 10.7. Automatic state detection of the Hibernate Reference Documentation:
You should use Merge() if you are trying to update objects that were at one point detached from the session, especially if there might be persistent instances of those objects currently associated with the session. Otherwise, using SaveOrUpdate() in that case would result in an exception.