EF Code First迁移:每个层次结构表错误

时间:2013-04-29 08:52:46

标签: entity-framework ef-code-first entity-framework-5

  • 通常我们可能需要在现有数据库中使用Entity Framework Code First
    • 现有数据库可能具有允许“Table Per Hierarchy”继承的结构。
  • 或者我们可以从一个看起来像这样的对象模型开始:

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; }
}

我们创建了初始迁移:

enable-migrations
add-migration Initial

迁移看起来像:

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);           
}

要生成此数据库,我们:

update-database

这导致我们可以像这样生成一个数据库。

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)

我们在生产中使用这个数据库一段时间......

现在我们想要添加与普通人不同的学生概念。

实体框架提供了三种表示继承的方法。在这种情况下,我们选择“Table Per Hierarchy”方法。

为了实现这种方法,我们按如下方式修改我们的POCO:

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; }
}

注意:

  • 只有学生才能访问StudentType媒体资源。
  • 我们未在Discriminator课程中指定Person属性。 EF Code First看到Student继承自Person,并会为我们在Person表中添加Discriminator列。

现在我们运行:

add-migration Person_TPH

我们得到了这个意想不到的输出。

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");
}

不应添加StudentType_Id列或索引。

我们可以通过添加'StudentMap'类来明确:

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

但没有快乐..

确实,如果我们删除数据库和所有迁移。 然后针对我们获得的新模型运行add-migration Initial

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);           
}

在此“正确”版本中,我们看到EF Code First迁移按预期使用StudentTypeId列。

问题

鉴于数据库已经存在,有没有办法告诉EF Code First迁移使用现有的StudentTypeId列。

证明问题的GitHub仓库在这里:

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 个答案:

答案 0 :(得分:1)

我发现有3个约定与类中显式外键的发现有关:

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

由于PrimaryKeyNameForeignKeyDiscoveryConvention上的主键仅为StudentType,因此Id无效。其他两个都会在StudentTypeId上匹配,所以只要你没有删除这两个,那么约定应该把它拿起来。

根据此问题(Foreign key navigation property naming convention alternatives),您还可以将[ForeignKey("StudentTypeId")]添加到StudentType上的Student媒体资源和[InverseProperty("StudentType")] Students } StudentType上的属性。

希望有所帮助。 :)