0

Question: is there a way to map a single foreign key to a number of mutually exclusive tables, based on a context?

Background...

In my specific example, I have the following domain graph, representing an insurance claim which can be against a vehicle or property:

public enum InvolvedPartyContext
{
    Vehicle = 1,
    Property = 2
}

public class Claim
{
    public virtual Guid Id { get; set; }
    public virtual InvolvedPartyContext InvolvedPartyContext { get; set; }
    public virtual Vehicle Vehicle { get; set; }    // set if Context = Vehicle
    public virtual Property Property { get; set; }  // set if Context = Property
}

public class Vehicle {  //... }

public class Property {  //... }

The SQL looks like this (notice the single foreign key InvolvedPartyId):

CREATE TABLE Claims ( 
 Id uniqueidentifier NOT NULL,
 InvolvedPartyContext int NOT NULL,
 InvolvedPartyId uniqueidentifier NOT NULL
)

CREATE TABLE Vehicles (
    Id uniqueidentifier NOT NULL,
    Registration varchar(20) NOT NULL
)

CREATE TABLE Properties (
    Id uniqueidentifier NOT NULL,
    PostCode varchar(20) NOT NULL
)

The Fluent NHibernate mapping file for Claim:

    public ClaimMap()
    {
        Id(x => x.Id);
        Map(x => x.InvolvedPartyContext).CustomTypeIs(typeof(InvolvedPartyContext));
        References(x => x.Vehicle, "InvolvedPartyId");
        References(x => x.Property, "InvolvedPartyId");
    }

This throws an "Invalid index {n} for this SqlParameterCollection with Count {m}" exception, since the same field (InvolvedPartyId) is mapped twice. A simple fix would be to create VehicleId and PropertyId fields, but in the real world there are many more contexts, so this isn't very flexible.

1 Answer 1

2

Personally, I wouldn't go with the design you have. Instead I'd create subclasses of your Claim class, VehicleClaim and PropertyClaim respectively.

public class VehicleClaim : Claim
{
  public virtual Vehicle Vehicle { get; set; }
}

Then change your mappings to use your InvolvedPartyContext column as a discriminator (the column which NHibernate uses to determine which class the row represents), and create subclass mappings for each subclass.

public class ClaimMap : ClassMap<Claim>
{
  public ClaimMap()
  {
    Id(x => x.Id);
    DiscriminateSubClassesOnColumn("InvolvedPartyContext");
  }
}

public class VehicleClaimMap : SubclassMap<VehicleClaim>
{
  public VehicleClaimMap()
  {
    DiscriminatorValue(1);
    References(x => x.Vehicle);
  }
}

If you really do want to run with what you've got, you should look into the any mappings; there isn't a lot of documentation on them, but you use the ReferencesAny method.

Sign up to request clarification or add additional context in comments.

1 Comment

Your suggestion of using discriminators is a good one, but unfortunately our design is fixed. Found some good ReferencesAny documentation on Ayende's blog, so I'll go down this route. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.