Search code examples
c#many-to-manyentity-framework-core

How to do many to many code first when foreign key does not contain full primary key


I'm trying to model a many to many relationship. This is my context:

modelBuilder.Entity<Description>(entity =>
{
    entity.HasKey(e => new { e.DescriptionID, e.Language });
});
modelBuilder.Entity<Company>(entity =>
{
    entity.HasKey(e => e.CompanyID);
});

modelBuilder.Entity<CompanyDescription>()
    .HasKey(cd => new { cd.CompanyID, cd.DescriptionID });
modelBuilder.Entity<CompanyDescription>()
    .HasOne(cd => cd.Company)
    .WithMany(c => c.CompanyDescriptions)
    .HasForeignKey(bc => bc.CompanyID);
modelBuilder.Entity<CompanyDescription>()
    .HasOne(cd => cd.Description)
    .WithMany(c => c.CompanyDescriptions)
    .HasForeignKey(bc => bc.DescriptionID);

CompanyDescription is the cross table. Both Company and Description contain

public ICollection<CompanyDescription> CompanyDescriptions { get; set; }

A company can have many descriptions and a description can have many companies. The way the database is currently set up is that a description does not contain a foreign key and a company only contains 'DescriptionID', but not language. So this 'foreign key' does not agree with the primary key of the description. Because of that I get:

The relationship from 'CompanyDescription.Description' to 'Description.CompanyDescriptions' with foreign key properties {'DescriptionID' : int} cannot target the primary key {'DescriptionID' : int, 'Language' : string} because it is not compatible. Configure a principal key or a set of compatible foreign key properties for this relationship.

What is the best way to fix this problem?

Edit, the model classes:

public partial class Company
{
    public int CompanyID { get; set; }
    public int DescriptionID { get; set; }
    public ICollection<CompanyDescription> CompanyDescriptions { get; set; }
}
public partial class Description
{
    public int DescriptionID { get; set; }
    public ICollection<CompanyDescription> CompanyDescriptions { get; set; }
}

The cross table:

public class CompanyDescription
{
    public int CompanyID { get; set; }
    public RN_Company Company { get; set; }
    public int DescriptionID { get; set; }
    public string Language { get; set; }
    public Description Description { get; set; }
}

Solution

  • As it is many-to-many relationship between Company and Description, Company should not contain either DescriptionID or both DescriptionID and Language. As you said company only contains 'DescriptionID', but not language. Then please remove the DescriptionID from the Company model class.

    Then your CompanyDescription entity should be as follows:

    public class CompanyDescription
    {
       public int CompanyID {get; set;}
    
       public int DescriptionID {get; set;}
       public string Language  {get; set;}
    
       public Company Company {get; set;}
       public Description Description {get; set;}
    }
    

    Then your CompanyDescription entity configuration should be as follows:

    modelBuilder.Entity<CompanyDescription>()
        .HasKey(cd => new { cd.CompanyID, cd.DescriptionID, cd.Language }); // <-- Here it is
    
    modelBuilder.Entity<CompanyDescription>()
        .HasOne(cd => cd.Company)
        .WithMany(c => c.CompanyDescriptions)
        .HasForeignKey(bc => bc.CompanyID);
    
    modelBuilder.Entity<CompanyDescription>()
        .HasOne(cd => cd.Description)
        .WithMany(c => c.CompanyDescriptions)
        .HasForeignKey(bc => new { bc.DescriptionID, bc.Language}); // <-- Here it is