2013-04-29 55 views
3
  • 通常我們可能需要使用實體框架代碼先用現有的數據庫。
    • 現有的數據庫可具有一種結構的允許「表每層次結構」繼承。
  • 或者我們可以用一個對象模型,看起來像開始:

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

我們創建初始遷移:EF的Code First遷移:表每一個分層的Bug

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) 

我們在生產中使用這個數據庫了一會兒......

現在我們想補充一點,從只是普通人不同學生的概念。

實體框架爲代表的繼承三種方法。在這種情況下,我們選擇「每個層次表」方法。

爲了實現這個方法,我們修改我們的波蘇斯如下:

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財產。
  • 我們不指定我們PersonDiscriminator屬性。 EF代碼第一次看到從PersonStudent繼承和會Discriminator列添加到Person表我們。

現在我們運行:

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代碼第一次遷移使用StudentTypeId列如預期。

問題

鑑於該數據庫已經存在,有沒有辦法告訴EF代碼優先遷移到使用現有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

有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)不過,你也可以在Student[InverseProperty("StudentType")]添加[ForeignKey("StudentTypeId")]StudentType屬性爲Students財產上StudentType

希望有所幫助。 :)