views:

192

answers:

1

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.

A: 

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
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
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
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
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 <type>") that would not give the error.
Dave Hanna