Question

This is a sample app I've concocted which produces the same behaviour as my actual much-more-complex app. I'm obviously missing some configuration aspect somewhere, but can't figure it out for the life of me. For some reason, each collection in my KID class is receiving its' own field and foreign key in the Sweet table....which just isn't necessary as far as I can see.....? How can I stop EF generating these?

Example classes, config and resulting migration code follows (NB, If I use TPT instead of TPH the extra fields aren't added and OwnerId is used as the relational field just fine)

My classes:

    public class Sweet
    {
        [Key]
        public int SweetId { get; set; }
        public string SweetName { get; set; }

        [ForeignKey("OwnerId")]
        public Kid Owner { get; set; }
        public int OwnerId { get; set; }
    }
    public class Chocolate : Sweet {}
    public class HardBoiled : Sweet {}
    public class Chewy : Sweet {}

    public class Kid
    {
        public int KidId { get; set; }
        public string FirstName { get; set; }
        public virtual ICollection<Chocolate> Chocolates { get; set; }
        public virtual ICollection<HardBoiled> BaggedSweeets { get; set; }
        public virtual ICollection<Chewy> PacketSweets { get; set; }
    }

My Configuration (Called from OnModelCreating)

public class SweetConfiguration : EntityTypeConfiguration<Sweet>
{
    public SweetConfiguration()
    {
        Map(m => m.ToTable("Sweet"));

        Map<Chocolate>(i => i.Requires("SweetType").HasValue(1));

        Map<Chewy>(i => i.Requires("SweetType").HasValue(2));

        Map<HardBoiled>(f => f.Requires("SweetType").HasValue(3));
    }
}

The generated Migration code:

    public override void Up()
    {
        CreateTable(
            "dbo.Kid",
            c => new
                {
                    KidId = c.Int(nullable: false, identity: true),
                    FirstName = c.String(),
                })
            .PrimaryKey(t => t.KidId);

        CreateTable(
            "dbo.Sweet",
            c => new
                {
                    SweetId = c.Int(nullable: false, identity: true),
                    SweetName = c.String(),
                    OwnerId = c.Int(nullable: false),
                    Kid_KidId = c.Int(), <--- DON'T NEED THIS
                    Kid_KidId1 = c.Int(), <--- OR THIS
                    Kid_KidId2 = c.Int(), <-- OR THIS!
                    SweetType = c.Int(),
                })
            .PrimaryKey(t => t.SweetId)
            .ForeignKey("dbo.Kid", t => t.Kid_KidId)  <!-- LIKEWISE FOR THESE THREE KEYS
            .ForeignKey("dbo.Kid", t => t.Kid_KidId1)
            .ForeignKey("dbo.Kid", t => t.Kid_KidId2)
            .ForeignKey("dbo.Kid", t => t.OwnerId, cascadeDelete: true)
            .Index(t => t.Kid_KidId)
            .Index(t => t.Kid_KidId1)
            .Index(t => t.Kid_KidId2)
            .Index(t => t.OwnerId);

    }

UPDATE:

As it would appear my current model isn't supported I have changed my Kid class like this:

public class Kid
{
    public int KidId { get; set; }
    public string FirstName { get; set; }
    public virtual ICollection<Sweet> Sweets { get; set; }

    [NotMapped]
    public ICollection<HardBoiled> BaggedSweets
    {
        get
        {
            return Sweets.OfType<HardBoiled>().ToList();
        }
    }
    ... and two more read-only NotMapped properties for the other collections...
}
Was it helpful?

Solution

You cannot use three collections in this model. EF expects that the inverse property and FK property (Owner and OwnerId) are declared directly in the classes the collections are refering to (i.e. in Chocolate, HardBoiled and Chewy) and not in the base class. To make it work and have only a single foreign key you can only define a single navigation collection in Kid that refers to the base class where Owner and OwnerId are declared in:

public class Kid
{
    public int KidId { get; set; }
    public string FirstName { get; set; }
    public virtual ICollection<Sweet> Sweets { get; set; }
}

(You can btw extract specific types from this collection using Sweets.OfType<Chocolate>(), etc.)

This is also the case for TPT. Is it possible that you had no SweetConfiguration and no DbSet<Sweet> in your TPT test? This would result in a model with no inheritance at all (from EF viewpoint) because all properties of the base class Sweet would have been added into the three child tables.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top