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
答案 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
上的属性。
希望有所帮助。 :)