views:

262

answers:

1

I'm trying to have a simple one to many relationship/hierarchy using NHibernate. I would like orphans to be deleted automatically but my current attempts to do so all result in an ObjectDeletedException. I'm wondering if someone can tell me what I'm doing incorrectly.

EDIT:

I should have specified that I'm loading a root Foo, then removing a child outside of the session, causing one or more children to be orphaned. The exception is occurring when I subsequently call SaveOrUpdate(root) in a second session. How can I rectify the difference in the list of children between the detached and modified object vs the object that is persisted in the database?

Sample code in question looks something like this:

Foo foo = new Foo();
Foo child1 = new Foo();
Foo child2 = new Foo();

foo.Children.Add(child1);
child1.Children.Add(child2);

// session #1
session.SaveOrUpdate(foo);

// so far, so good

// outside of any session
foo.Children.Clear();

// session #2
PutFoo(foo); // results in ObjectDeletedException

The object being persisted:

class Foo
{
    private IList<Foo> children = new List<Foo> children;

    public virtual int Id { get; private set; }
    public IList<Foo> Children
    {
        get { return children; }
        set { children = value; }
    }
}

The FluentNHibernate mapping:

class FooMap : ClassMap<SyncDir>
{
    public FooMap()
    {
        Id(x => x.Id);
        base.HasMany(x => x.Children).Cascade.AllDeleteOrphan();
     }
}

The method used to persist an object of type Foo:

void PutFoo(Foo foo)
{
    using (var session = factory.OpenSession())
    using (var transaction = factory.BeginTransaction())
    {
        session.SaveOrUpdate(foo);
        transaction.Commit();
    }
}
+1  A: 

What I always do, is create a bidrectional relationship.

So, this means that the Children have a reference to their parent. When removing a child from the collection, I also set the reference to the parent to NULL.

In the mapping , you then also have to indicate the 'inverse' end of the relationship. I also never expose the collection 'as is' outside the class.

Thus, I mostly do it like this:

public class Foo
{
   private ISet<Bar> _bars = new HashSet<Bar>();

   public ReadOnlyCollection<Bar> Bars { get return new List<Bar>(_bars).AsReadOnly(); }

   public void AddBar( Bar b )
   {
      b.Parent = this;
      _bars.Add (b);
   }

   public void RemoveBar( Bar b )
   {
      b.Foo = null;
      _bars.Remove (b);
   }
}


public class Bar
{
     public Foo Parent { get; set; }
}

Then, in the mapping I set the 'inverse' end on the collection. So, this means that in my mapping (I still use xml files to specify the mapping), I set the inverse=true attribute on the Bars collection of the Foo class.

Frederik Gheysels
i added the Parent and Add()/Remove() methods, but i'm still getting the exception when calling SaveOrUpdate() on the root node after removing a child. I should mention that I load the root, then call Remove() outside of the session. It's only then when remove a child and call SaveOrUpdate() from a second session that the exception occurs.
anthony