Tuesday, March 11, 2008

NHibernate Gotchas - Orphans and one-to-one

I'd previously posted about NHibernate Gotchas but we just came accross a big one, cascade options on one-to-one.

Essentially we'd used the cascade option "all-delete-orphan" on a which we thought was fine. NHibernate didn't complain and the schema indicated it was a valid option.

Unfortunately the cascade option was totally ignored and indeed after quite a bit of effort we found that the docs do indicate its not supported an its a known issue.

Workarounds, not sure. Deleting the one-to-one from the ISession directly is not an option for us in our POCO domain so we may need to look at other mappings.

Worth knowing anyway!

Share This - Digg It Save to del.icio.us Stumble It! Kick It DZone

6 comments:

  1. Did you ever find a reasonable solution?

    ReplyDelete
  2. Did you ever find a reasonable solution?

    ReplyDelete
  3. Afraid not, none of the solutions we found were particularly good...

    ReplyDelete
  4. What did you ultimately do to as a workaround? The only solution I can think of is to either first delete the child then save the entity or sync the child Id's. Ugh.

    ReplyDelete
  5. After spinning on this for a while, I thought I would share my workaround:

    Context:
    * Registration has 1 Payment.
    * A Payment could be a CreditCardPayment, CashPayment, MoneyOrderPayment or CheckPayment.
    * A Payment has 1 Payee
    * A Payee could be an IndividualPayee, OrganizationPayee, RegistrantPayee

    So in the Registration mapping I have:
    <many-to-one class="Payment" column="[PaymentId]" name="payment" access="field" cascade="all-delete-orphan" lazy="false" fetch="join" />

    in the Payment mapping I have:
    <one-to-one constrained="true" name="registration" access="field" property-ref="payment" />
    <many-to-one class="Payee" column="[PayeeId]" name="payee" access="field" cascade="all-delete-orphan" fetch="join" lazy="false" />

    Ideally, I wanted to map Payment and Payee as components of a Registration, but as you know, there is no component inheritance in NHibernate.

    My API works like this:
    Registration r = new Registration());
    r.Payment = new CashPayment();

    In order to handle the orphans, I bascially had to create a query to get all of the existing "orphans" and then delete those.

    //delete all payee orphans
    Repository<Payee>.DeleteAll(new Query<Payee>(@"from Payee payee
    where payee not in(select payment.payee from Payment payment where payment.payee is not null)"));

    //delete all payment orphans
    Repository<Payment>.DeleteAll(new Query<Payment>(@"from Payment p
    where p not in(select r.payment from Registration r where r.payment is not null)"));

    Additionally, I had to first "save" my registration in its own transaction. Then run the "delete orphans" code its own transaction.

    To bad the "all-delete-orphan" doesn't work for many-to-one or one-to-one mappings!

    ReplyDelete
  6. Not very nice but you could use a simply if-statement to simply not execute the nhibernate delete statement if the object is just an in-memory object instead of a persistent one? Yes that is kind of a hack, but better than having to alter the mappings.

    ReplyDelete