EF代码首次迁移:每层次错误的表 [英] EF Code First migrations: Table Per Hierarchy Bug

查看:71
本文介绍了EF代码首次迁移:每层次错误的表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


  • 我们通常可能需要先使用现有数据库使用实体框架代码。


    • 现有的数据库可能有一个结构允许每层次结构继承。

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

    要生成此数据库,我们:

    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.

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

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

    注意:


    • 只有学生可以访问 StudentType 属性。

    • 我们不指定<$我们的 Person 类中的c $ c>判别器属性。 EF代码首先看到学生继承自 Person ,并将添加一个鉴别器列给我们的Person表。

    • 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.

    现在我们运行:

    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类来显式:

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

    但没有喜悦..

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

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

    在这个正确版本中,我们看到EF Code First迁移使用 StudentTypeId 列预期。

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

    问题

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

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

    演示问题的GitHub回购如下:

    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
    


    推荐答案

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

    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
    

    PrimaryKeyNameForeignKeyDiscoveryConvention 不会在这里,因为 StudentType 中的主键只是 Id 。另外两个都会匹配 StudentTypeId ,所以只要你没有删除这两个,那么这些约定应该会被接受。

    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.

    根据这个问题(外键导航属性命名但是,您也可以将 [ForeignKey(StudentTypeId)] 添加到 StudentType 学生 [InverseProperty(StudentType)] 学生 property on StudentType

    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.

    希望有所帮助。 :)

    这篇关于EF代码首次迁移:每层次错误的表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆