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 には、継承を表すための 3 つのアプローチが用意されています。この場合、「階層ごとのテーブル」アプローチを選択します。

このアプローチを実装するために、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 は、Person テーブルStudentから継承され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