views:

110

answers:

2

I'd like one of my entities to have a one-to-one relationship with a class hierarchy. Think of it like a Strategy pattern, where each strategy needs different parameters to be persisted. I tried using a combination of OneToOne and JoinedBase/JoinedKey, but I've come across a problem.

With this combination, the primary key of the main entity also appears as the primary key of the table representing the root class in the hierarchy, and as the primary key of the subclass:

        Order    --------------- TaxCalculator
([PrimaryKey]Id = 1234)        ([PrimaryKey(PrimaryKeyType.Foreign)]OrderId = 1234) 
                                      ^
                                      |
                                      |
                             UkTaxCalculator
                      ([JoinedKey]UkTaxCalculatorId = 1234)

I can persist this fine, but then I can't change which subclass of TaxCalculator I have. When I do something like:

order.TaxCalculator = new OverseasTaxCalculator(order);

then try to flush, then ActiveRecord/NHibernate (understandably) gets unhappy that there are now two TaxCalculators with Id = 1234.

I can get around this by replacing the OneToOne with a HasMany/BelongsTo, and hiding the multiplicity from users of the Order object, but I'm interested to know if it's possible to do this with OneToOne.

There's a full code example on github. This code throws an exception when the second SessionScope is disposed. If you clone the project, it should run out-of-the-box.

+1  A: 

Hi Alex, first of all i am sorry, but i did not tried my solution. It is to late and i really need my sleep ;-). I think the only way the one-to-one could work would be a 'table-per-hierarchy'-approach using a discriminator column instead of table-per-subclass. Maybe this will enable you to morph the existing object to another subclass. An other way, something like a polymorphic delete-orphan unfortunately is not supported as you stated. So i'll guess this would be your (very) last option.

But if this fails why don't you map it as a one-to-many instead of many-to-one with a foreign key in the order table, reusing the TaxCalculators? I would imagine them as quite static.

Interesting idea though: polymorphic delete-orphan.

zoidbeck
Thanks zoidbeck. Good to hear that it isn't just me that can't make it work. Sounds like we could make it work for table-per-hierarchy (SingleTableInheritance). We'd preferred table-per-class (ClassTableInheritance) so that the base table wouldn't need to be changed to add new Calculators (Open/Closed principle).In my real domain (not Orders and TaxCalculators), each 'Order' has different parameters for its 'TaxCalculator', so your other idea won't apply. My bad for not choosing a better sample domain!
Alex Scordellis
A: 

Hi Alex,

We do something very similar to what you are trying to do. I think it's your combination of one-to-one and the joined key that's causing the problem. Try this:

[ActiveRecord, JoinedBase]
public class TaxCalculator
{
 protected int TaxCalculatorId;

 [PrimaryKey]
 public virtual int Id
 {
  get { return TaxCalculatorId; }
  set { TaxCalculatorId = value; }
 }

 // common tax calculation fields, methods etc...
}

[ActiveRecord]
public class OverseasTaxCalculator : TaxCalculator
{
 [JoinedKey]
 public override int Id
 {
  get { return TaxCalculatorId; }
  set { TaxCalculatorId = value; }
 }

 // overseas tax calculation specific methods, properties etc...
}
Jonathan Moffatt
Am I missing something? Your solution seems to be fine for the class hierarchy, but I don't see where it addresses the one to one relationship. That's a key part of my requirements.
Alex Scordellis