Vicky
Vicky

Reputation: 1117

NHibernate one to many mapping not saving children only sometimes

Saving a child of a parent object via nhibernate only sometimes results in the not inserting the child in the table. This works most of the times and only a couple of times out of many thousands, the child was missing.

This is in a large multi-threaded application.

The following is the child mapping :

<class name="Child" table="children" lazy="false" optimistic-lock="all" dynamic-update="true">

<id name="Id" column="id">
  <generator class="native" />
</id>

<many-to-one name="MyParent" class="Parent" column="parent_id" not-null="true"/>
...
...

and the Child Class :

public class Child
{
    public long Id { get; set; }
    public Parent MyParent { get; set; }
...
..

The Parent Mapping :

<id name="Id" column="id">
  <generator class="native" />
</id>

<set name="Children" table="children" cascade="none" lazy="true" inverse="true">
  <key column="parent_id" not-null="true" />
  <one-to-many class="Child" />
</set>
...
...

and the following is the Parent Class

public class Parent
{
    public long Id { get; set; }
    public ICollection<Child> Children { get; set; }
...
...
..
}

The code to add a parent and a child is shown below :

var theParent = new Parent 
{ 
   ...
}
...
// start the transaction if not begun by the caller
// a few lines later...
Session.Save(theParent);
// Commit transaction only if this method began the transaction...

...

var child = new Child 
{ 
    MyParent = theParent, 
    ...
    ...
}
...
// start the transaction if not begun by the caller
// a few lines later...
Session.Save(child);
// Commit transaction only if this method began the transaction...
// make sure the two-way associations are updated!
if (!child.MyParent.Children.Contains(child))
        child.MyParent.Children.Add(child);
...

The mechanism to check if transaction has been begun by the caller doesn't seem to have any bugs and the caller doesn't roll back without throwing an exception and logging it. I do not find any traces of the transaction being rolled back.

  1. Is there a problem with the "cascade" option in the Parent mapping ?
  2. Is it because the transaction commit happens before the adding of the child in the collection of the parent ?

In either case, why is the child missing in only few cases. This is very difficult to handle and could cause a lot of problems in production silently. What could be the cause due to which this has happened ?

Upvotes: 0

Views: 502

Answers (1)

Fr&#233;d&#233;ric
Fr&#233;d&#233;ric

Reputation: 9864

  1. No.
  2. No.

I would bet your transaction handling mechanism has a flaw and the transaction is sometime not committed at all. It ends up garbage collected, causing it to be disposed and roll-backed silently. This happens in case of session Close occurring before transaction commit too.

You may track it by logging transaction starts/commits/rollbacks with relevant context information for matching starts with commits and rollbacks.

This can be quite tricky. What is a relevant context information? It depends heavily on application type. In a web application under IIS, thread context is bad, call context is bad. They all get lost in case of asp.net thread agility switch, which occurs under load. Only HTTP context is preserved in most cases. (And beware of async await with "callback" configured for running without any context...)

Upvotes: 1

Related Questions