EF Code First migrations: Table Per Hierarchy Bug

你说的曾经没有我的故事 提交于 2019-12-22 09:56:02

问题


  • Often we might need to use Entity Framework Code First with an existing database.
    • The existing database may have a structure the allows "Table Per Hierarchy" inheritance.
  • Or we might start with an object model that looks like:

public partial class Person {
    public int Id { get; set; }
    public string Discriminator { get; set; }
    public string Name { get; set; }
    public Nullable<int> StudentTypeId { get; set; }
    public virtual StudentType StudentType { get; set; }
}

public partial class StudentType {
    public StudentType() {
        this.People = new List<Person>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Person> People { get; set; }
}

We create the initial migration:

enable-migrations
add-migration Initial

The migration looks like:

public override void Up()
{
    CreateTable(
        "dbo.Person",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                Discriminator = c.String(maxLength: 4000),
                Name = c.String(maxLength: 4000),
                StudentTypeId = c.Int(),
            })
        .PrimaryKey(t => t.Id)
        .ForeignKey("dbo.StudentType", t => t.StudentTypeId)
        .Index(t => t.StudentTypeId);

    CreateTable(
        "dbo.StudentType",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                Name = c.String(maxLength: 4000),
            })
        .PrimaryKey(t => t.Id);           
}

To generate this database we:

update-database

This results in a database that we could have generated like this.

create table Person(
  Id int Identity(1,1) Primary key,
  Discriminator nvarchar(4000) null,
  StudentTypeId int null,
)

create table StudentType(
  Id int Identity(1,1) Primary key,
  Name nvarchar(4000) not null
)

alter table Person 
add constraint StudentType_Person
foreign key (StudentTypeId)
references StudentType(Id)

We use this database in production for a while...

Now we want to add the concept of students that are different from just regular people.

Entity Framework provides three approaches for representing inheritance. In this case we choose the "Table Per Hierarchy" approach.

To implement this approach we modify our POCOs as follows:

public class Person {
   public int Id { Get; set; }
   public string Name { get; set }
}

public class Student : Person {
  public virtual StudentType StudentType { get; set; }
  public int? StudentTypeId { get; set; }
}

public class StudentType {
    public StudentType() {
        Students = new List<Student>();
    }

    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Student> Students { get; set; }
}

Note:

  • Only Students have access to the StudentType property.
  • We don't specify the Discriminator property in our Person class. EF Code First sees that Student inherits from Person and will add a Discriminator column to the Person table for us.

Now we run:

add-migration Person_TPH

And we get this unexpected output.

public override void Up()
{
    AddColumn("dbo.Person", "StudentType_Id", c => c.Int());
    AlterColumn("dbo.Person", "Discriminator", c => c.String(nullable: false, maxLength: 128));
    AddForeignKey("dbo.Person", "StudentType_Id", "dbo.StudentType", "Id");
    CreateIndex("dbo.Person", "StudentType_Id");
}

It should not be adding the StudentType_Id column or index.

We can be explicit by adding the 'StudentMap' class:

public class StudentMap : EntityTypeConfiguration<Student> {
    public StudentMap() {
        this.HasOptional(x => x.StudentType)
            .WithMany()
            .HasForeignKey(x => x.StudentTypeId);
    }
}

But no joy..

Indeed, if we delete the database and all the migrations. Then run add-migration Initial against our new model we get:

public override void Up()
{
    CreateTable(
        "dbo.Person",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                Name = c.String(maxLength: 4000),
                StudentTypeId = c.Int(),
                Discriminator = c.String(nullable: false, maxLength: 128),
            })
        .PrimaryKey(t => t.Id)
        .ForeignKey("dbo.StudentType", t => t.StudentTypeId)
        .Index(t => t.StudentTypeId);

    CreateTable(
        "dbo.StudentType",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
                Name = c.String(nullable: false, maxLength: 100),
            })
        .PrimaryKey(t => t.Id);           
}

In this "correct" version we see that EF Code First migrations uses the StudentTypeId column as expected.

Question

Given that the database already exists, is there a way to tell EF Code First migrations to use the existing StudentTypeId column.

The GitHub repo that demonstrates the problem is here:

https://github.com/paulyk/ef_code_first_proof_of_tph_bug.git

Git tags
1_add_migration_Initial
2_add_migration_person_TPH
3_add_studentMap

回答1:


There are 3 conventions that I found that relate to the discovery of explicit foreign keys in the class:

System.Data.Entity.ModelConfiguration.Conventions.NavigationPropertyNameForeignKeyDiscoveryConvention
System.Data.Entity.ModelConfiguration.Conventions.PrimaryKeyNameForeignKeyDiscoveryConvention
System.Data.Entity.ModelConfiguration.Conventions.TypeNameForeignKeyDiscoveryConvention

The PrimaryKeyNameForeignKeyDiscoveryConvention would not help here since the primary key on StudentType is just Id. The other two would both match on StudentTypeId though, so as long as you aren't removing both of those, the conventions should pick it up.

According to this question (Foreign key navigation property naming convention alternatives) though, you can also add [ForeignKey("StudentTypeId")] to the StudentType property on Student and [InverseProperty("StudentType")] to the Students property on StudentType.

Hope that helps. :)



来源:https://stackoverflow.com/questions/16274653/ef-code-first-migrations-table-per-hierarchy-bug

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!