views:

1896

answers:

2

hi

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 neither in Person nor in Event.

when i try to delete an entry from PersonEventRegistration, i get 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?

A: 

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
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
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
... so the correct cascade for a `many-to-one` is usually `save-update`.
Daniel Schilling
A: 

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