1

The following entities is my code first which PersonParameter is an abstract class and Shirt and Shoes are inherited from it with typ(1,2)

[Table("Person")]
public class Person
{
    [Key]
    public int id { get; set; }
    public string Name { get; set; }

    public int? shirtID { get; set; }
    public int? shoesID { get; set; }

    [ForeignKey("shirtID")]
    public Shirt Shirt { get; set; }
    [ForeignKey("shoesID")]
    public Shoes Shoes { get; set; }
}

[Table("PersonParameter")]
public abstract class PersonParameter
{
    public int id { get; set; }
    public string Title { get; set; }
    public string Value { get; set; }

    public List<Person> Persons { get; set; }
}

public class Shirt : PersonParameter
{

}
public class Shoes : PersonParameter
{

}

and for model dbcontext

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<PersonParameter>()
        .Map<Shirt>(p => p.Requires("typ").HasValue(1))
        .Map<Shoes>(p => p.Requires("typ").HasValue(2));
}

but the above codes will create unwanted field PersonParameter_id in Person table:

public override void Up()
{
    CreateTable(
        "dbo.PersonParameter",
        c => new
            {
                id = c.Int(nullable: false, identity: true),
                Title = c.String(),
                Value = c.String(),
                typ = c.Int(nullable: false),
            })
        .PrimaryKey(t => t.id);

    CreateTable(
        "dbo.Person",
        c => new
            {
                id = c.Int(nullable: false, identity: true),
                Name = c.String(),
                shirtID = c.Int(),
                shoesID = c.Int(),
                PersonParameter_id = c.Int(),
            })
        .PrimaryKey(t => t.id)
        .ForeignKey("dbo.PersonParameter", t => t.shirtID)
        .ForeignKey("dbo.PersonParameter", t => t.shoesID)
        .ForeignKey("dbo.PersonParameter", t => t.PersonParameter_id)
        .Index(t => t.shirtID)
        .Index(t => t.shoesID)
        .Index(t => t.PersonParameter_id);

}

how can I solve it (PersonParameter_id) I did some FluentApi with HasOptional.WithMany but didn't solve.

EDIT
After some test, found that for non abstract class it create extra id too and the one solution for fixing that issue is removing navigation property from parameter class and adding it to the inheritance classes (shirt and shoes)

Hamid
  • 817
  • 1
  • 13
  • 29

1 Answers1

1

The reason you end up with one additional FK on Persons table is because of the Persons property on PersonParameter class. Basically EF cannot relate that to any of the current two relationships defined (Shirt and Shoes) and figured that Persons property must have been a third relationship between Persons and PersonParamter tables and therefore creates a third FK (PersonParameter_id) to create this relationship. As a result, you end up with 3 uni-directional Many to One relationships from PersonParameter to Persons.

To fix this, you need to explicitly tell EF that Persons is in fact the Inverse Property of Shoes and Shirts since there is no way that EF can pick that up automatically. You can do that with fluent API or by Annotations like this:

[Table("PersonParameter")]
public abstract class PersonParameter
{
    public int id { get; set; }
    public string Title { get; set; }
    public string Value { get; set; }
}

public class Shirt : PersonParameter
{
    [InverseProperty("Shirt")]
    public List<Person> Persons { get; set; }
}

public class Shoes : PersonParameter
{
    [InverseProperty("Shoes")]
    public List<Person> Persons { get; set; }
}


You can also achieve this by using the Fluent API:

modelBuilder.Entity<Shirt>()
    .HasMany(s => s.Persons)
    .WithOptional(p => p.Shirt)
    .HasForeignKey(p => p.shirtID);

modelBuilder.Entity<Shoes>()
    .HasMany(s => s.Persons)
    .WithOptional(p => p.Shoes)
    .HasForeignKey(p => p.shoesID);
Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83
  • but there are two FK in Person for PersonParameter (ShirtID and ShoesID) so why it needs additional FK? – Hamid May 09 '16 at 05:00
  • Thanks for the comment. I updated my answer accordingly. – Morteza Manavi May 09 '16 at 15:41
  • Thanks. I did it before but it is important that have that navigation property inside main class (PersonParameter) and not inside its inheritance class because it should be a basic class. Also I have some test for declaring public List Persons { get; set; } as an abstract property and I believe there is a solution with Fluent API which telling ef about those inverse properties. – Hamid May 09 '16 at 18:29
  • I updated my answer with the fluent API but I don't think there is a way that EF lets you have a Persons property in the base class and define 2 different relationships on that for each subclass, unfortunately. – Morteza Manavi May 09 '16 at 19:27