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

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)

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

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

Entity Framework 提供了三种表示继承的方法。在这种情况下,我们选择“按层次结构表”方法。

为了实现这种方法,我们修改了我们的 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属性。PersonEF 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
4

1 回答 1

1

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

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

在这里PrimaryKeyNameForeignKeyDiscoveryConvention不会有帮助,因为主键StudentType只是Id. 不过,其他两个都将匹配StudentTypeId,因此只要您不删除这两个,约定就应该接受它。

根据这个问题(外键导航属性命名约定替代方案),您也可以添加到[ForeignKey("StudentTypeId")]属性StudentTypeonStudent和属性 on 。[InverseProperty("StudentType")]StudentsStudentType

希望有帮助。:)

于 2013-07-02T17:40:04.803 回答