6

I have following model:

<class name="Person" table="Person" optimistic-lock="version">
  <id name="Id" type="Int32" unsaved-value="0">
    <generator class="native" />
  </id>
  <!-- plus some properties here -->
</class>

<class name="Event" table="Event" optimistic-lock="version">
  <id name="Id" type="Int32" unsaved-value="0">
    <generator class="native" />
  </id>
  <!-- plus some properties here -->
</class>

<class name="PersonEventRegistration" table="PersonEventRegistration" optimistic-lock="version">
  <id name="Id" type="Int32" unsaved-value="0">
    <generator class="native" />
  </id>
  <property name="IsComplete" type="Boolean" not-null="true" />
  <property name="RegistrationDate" type="DateTime" not-null="true" />
  <many-to-one name="Person" class="Person" column="PersonId" foreign-key="FK_PersonEvent_PersonId" cascade="all-delete-orphan" />
  <many-to-one name="Event" class="Event" column="EventId" foreign-key="FK_PersonEvent_EventId" cascade="all-delete-orphan" />
</class>

There are no properties pointing to PersonEventRegistration either in Person nor in Event.

When I try to delete an entry from PersonEventRegistration, I get the following error:

"deleted object would be re-saved by cascade"

The problem is, I don't store this object in any other collection - the delete code looks like this:

public bool UnregisterFromEvent(Person person, Event entry)
{
    var registrationEntry = this.session
        .CreateCriteria<PersonEventRegistration>()
        .Add(Restrictions.Eq("Person", person))
        .Add(Restrictions.Eq("Event", entry))
        .Add(Restrictions.Eq("IsComplete", false))
        .UniqueResult<PersonEventRegistration>();

    bool result = false;
    if (null != registrationEntry)
    {
        using (ITransaction tx = this.session.BeginTransaction())
        {
            this.session.Delete(registrationEntry);
            tx.Commit();
            result = true;
        }
    }
    return result;
}

What am I doing wrong here?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Greg
  • 565
  • 1
  • 5
  • 13

2 Answers2

3

As far as I know, cascade="all-delete-orphan" belongs on the collection mapping element, not the many-to-one. You haven't shown the other two parts of your mapping so I can't say for sure but this is possible (likely) the problem.

I think Person should look something like:

<!-- other properties -->
<set name="Events" inverse="true" cascade="all-delete-orphan">
    <key column="Person_id" />
    <one-to-many class="PersonEventRegistration" />
</set>

Event:

<!-- similar mapping for Event -->

PersonEventRegistration:

<!-- other properties -->
<many-to-one name="Person" class="Person" column="PersonId" foreign-key="FK_PersonEvent_PersonId" cascade="delete" <!-- or many ="all" ? --> />

Actually, the above could be conflicting cascades (which might be what you have). So really, my answer is two things:

  1. cascade="all-delete-orphan" has no meaning on many-to-one.
  2. Make sure you have really thought-through how you are working with your entities and how they should cascade their operations.
Stuart Childs
  • 3,295
  • 1
  • 19
  • 11
  • i didn't notice that post didn't contain all formatting... anyway - the Event and the Person mappings do not contain any information about PersonEventRegistration. this class is used to support many-to-many mapping + some extra attributes (i didn't find any example where nhibernate would support such scenario). i'll try cascade="delete" anyway. – Greg Jun 02 '09 at 05:33
  • 2
    Your recommendations are spot-on. It's more than just "`all-delete-orphan` has no meaning". Cascading a delete from a child up to a parent is usually _evil_. That's what `all` and `all-delete-orphan` would do on an `many-to-one`. Deleting the `PersonEventRegistration` with those cascades would cause NHibernate to try to delete the `Person` and the `Event` as well, which is surely not what Greg intends. And that's why the delete fails - because the Person and the Event are still referenced elsewhere, so NHibernate can't delete them. – Daniel Schilling Jan 27 '10 at 22:34
  • ... so the correct cascade for a `many-to-one` is usually `save-update`. – Daniel Schilling Jan 27 '10 at 22:36
  • Thanks Stuart and Daniel for this comment! You saved my day. But what i'd like to add is, that you have to make shure your overridden GetHashCode() and Equals() work correct. I run into an error, where nhibernate could not delete the asocciations correct. – MoJo2600 Jan 14 '11 at 09:09
1

Try de-referencing Person and Event in the delete:

    public bool UnregisterFromEvent(Person person, Event entry)
    {
        var registrationEntry = this.session
            .CreateCriteria<PersonEventRegistration>()
            .Add(Restrictions.Eq("Person", person))
            .Add(Restrictions.Eq("Event", entry))
            .Add(Restrictions.Eq("IsComplete", false))
            .UniqueResult<PersonEventRegistration>();

        bool result = false;
        if (null != registrationEntry)
        {
            using (ITransaction tx = this.session.BeginTransaction())
            {
                registrationEntry.Person = null;
                registrationEntry.Event = null;
                this.session.Delete(registrationEntry);
                tx.Commit();
                result = true;
            }
        }
        return result;
    }

Also, I wasn't aware that you could add a restriction on an object, I would have written this using aliases and IDs.

            .Add(Restrictions.Eq("Person", person))
            .Add(Restrictions.Eq("Event", entry))
Jamie Ide
  • 48,427
  • 16
  • 81
  • 117