0

I have a simple parent/child tables. Contract is the parent table. Each contract can have multiple units.

Here is my C# class definitions (simplified):

    public class Contract : EntityWithIntID
{
    public virtual string ContractNum { get; set; }
    public virtual IList<Unit> Units { get; protected set; }
    public virtual int NumberOfUnits
    {
        get { return Units.Count; }
    }
 }

    public class Unit : EntityWithIntID
{
    <various Unit physical data fields>
}

I'm using FluentNHibernate with AutoMapping. Here is my AutoMapping class:

   public static AutoPersistenceModel GetMappings()
    {
        AutoPersistenceModel returnModel = AutoMap.AssemblyOf<Contract>()
            .IgnoreBase(typeof(EntityWithIntID))
            .Where(type => type.BaseType == typeof(EntityWithIntID) )
            .Conventions.Add(typeof(PluralTableNamesConvention))
            .Conventions.Add(typeof(CascadeAllConvention))
            .Override<Contract>(map =>map.HasMany(cont =>cont.Units).Inverse())
            .Override<Contract>(map=>map.Map(cont => cont.ContractNum).Not.Nullable().Unique())
        ;
        return returnModel;
    }
}

Here are the HBM.XML files that Fluent generates:

(for the Units table):

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="InterfaceDB.Unit, InterfaceDB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Units">
   <id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="hilo">
        <param name="max_lo">10</param>
      </generator>
   </id>
<!-- physical data property elements removed for brevity -->
  </class>
</hibernate-mapping>

(and for the Contracts table):

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="InterfaceDB.Contract, InterfaceDB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Contracts">
    <id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="hilo">
        <param name="max_lo">10</param>
      </generator>
    </id>
    <property name="ContractNum" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ContractNum" not-null="true" unique="true" />
    </property>
    <bag cascade="all" inverse="true" name="Units">
      <key>
        <column name="Contract_id" />
      </key>
      <one-to-many class="InterfaceDB.Unit, InterfaceDB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
</hibernate-mapping>

For my unit testing purposes, I have a "Clear()" method in each of the repositories so I can start with a known empty data base. It simply does a

Session.Delete("from Unit");

followed by a Debug.Assert(Repository.GetCount() == 0);

My problem is that when I do a UnitsRepository.Clear(), I get an NHibernate.ObjectDeleteException: deleted object would be re-saved by cascade (remove deleted object from associations)

I have googled that error, and found a number of articles about it, but nothing suggested seems to work. I have added the ".Inverse()" on the parent mapping. I have tried changing the cascading from "All" to "SaveUpdate" (it was never set to "AllDeleteOrphans", which some posts cited as the problem.) I'm clearing all the repositories, and I tried wrapping that whole thing in a transaction. I tried adding a flush after the Session.Delete. I tried clearing the parent repository first, then the child. Nothing gets rid of the error.

Any help would be appreciated.

Seth Moore
  • 3,575
  • 2
  • 23
  • 33
Dave Hanna
  • 2,491
  • 3
  • 32
  • 52

1 Answers1

0

You don't show it in your mappings but presumably Unit has a reference to its parent Contract. In order to delete a Unit you have to set the reference to the Contract to null and, depending on your mapping, remove it from the Contract's Units collection.

Jamie Ide
  • 48,427
  • 16
  • 81
  • 117
  • Jamie, There is not a reference to the Contract table in either the Unit POCO or in the HBM.XML file. The schema created does include a Contract_Id column in the Unit table, which, I take it, comes from the bag/key/column name="Contract_Id" element in the Contract HBM.XML file. But if there were, what if I just want to clear all the tables? I.e., do the equivalent of a "DELETE FROM Units" and "DELETE FROM Contracts". Do I have to go through each element in a for loop and remove each element? – Dave Hanna Mar 05 '10 at 15:32
  • That error normally occurs when you delete an object that is still referenced by another object. When the ISession is flushed the reference causes the deleted object to be re-saved. Since you have cascade=all it should be enough to delete Contracts and that will delete all the Units. – Jamie Ide Mar 05 '10 at 22:25
  • I'll verify it when I get back to work on Monday, but I'm pretty sure I tried calling Clear on the ContractsRepository first, and I still got the same error. – Dave Hanna Mar 06 '10 at 12:02
  • Well, not really the answer that I wanted, but apparently you DO have to go through each element and delete them individually. I could not find any combination or order of session.Delete("from ") that would not give the error. – Dave Hanna Mar 12 '10 at 13:22