Mark
Mark

Reputation: 68

Entity Framework 4.1 Code First, Combined Primary Key as a Foreign Key

I'm having an issue with Entity Framework where I have something similar to the following mockup:

public class ClassA
{
  public int ClassAID { get; set; }
}

public class ClassB
{
  public int ClassBID { get; set; }
}

public class ClassC
{
  public int ClassAID { get; set; } //Foreign Keys combined as Primary Key
  public int ClassBID { get; set; }

  public virtual ClassA SomeA { get; set; }
  public virtual ClassB SomeB { get; set; }
  public virtual ClassD SomeD { get; set; }
}

public class ClassD
{
  public int ClassAID { get; set; } //Primary Key and also references Class C Primary Key
  public int ClassBID { get; set; }

  public virtual ClassC SomeC { get; set; }
}

ClassD is where I am having a problem, I want the properties that represent the primary key on ClassC to be the primary key on ClassD, but also be a foreign key reference. (Assume the property names above are the same as the table column names)

In the underlying database the corresponding tables for ClassC and ClassD have a one-to-one relationship, where as ClassA to ClassC and ClassB to ClassC is a one-to-many relationship.

When it comes to Entity Framework however it cannot seem to handle multiple properties of the same name acting as a primary key and foreign key at the same time, in the underlying SQL that it generates, I can see it looking for columns ClassD_ClassAID, ClassD_ClassBID - is there a way using the Model Configuration to specify the correct mapping?

I have tried:

this.HasKey(c => new { c.ClassAID, c.ClassBID });
this.HasRequired(c => c.ClassC)
.WithRequiredDependent();

I've also tried:

this.HasKey(c => new { c.ClassAID, c.ClassBID });
this.HasRequired(c => c.ClassC)
.WithRequiredDependent()
.Map(m => m.MapKey("ClassAID", "ClassBID"));

Any attempt so far to introduce a mapping is met with

'Property name xxx already exists in the metadata'.

Upvotes: 1

Views: 3592

Answers (1)

Slauma
Slauma

Reputation: 177133

I am not sure if the following solves your problem because I don't understand where the exception you mentioned could come from:

modelBuilder.Entity<ClassC>()
    .HasKey(c => new { c.ClassAId, c.ClassBId });

modelBuilder.Entity<ClassD>()
    .HasKey(d => new { d.ClassAId, d.ClassBId })
    .HasRequired(d => d.SomeC)
    .WithRequiredDependent(c => c.SomeD);

The important part here is that you specify the navigation property on the other side of the relationship in WithRequiredDependent. If you use the parameterless overload EF will create a second relationship between ClassC and ClassD and SomeD would belong to this relationship and not to the one you are configuring.

I you don't specify any further mapping for ClassA and ClassB EF will create the following three relationships based on the mapping above and on conventions:

  • ClassA -> ClassC (FK = ClassAId in ClassC)
  • ClassB -> ClassC (FK = ClassBId in ClassC)
  • ClassC -> ClassD (FK = ClassAId, ClassBId in ClassD)

So, ClassD has a composite primary key which is a composite foreign key to ClassC at the same time.

Upvotes: 4

Related Questions