问题描述
- 我们通常可能需要先使用现有数据库使用实体框架代码。
- 现有的数据库可能有一个结构允许每层次结构继承。
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 ourPerson
class. EF Code First sees thatStudent
inherits fromPerson
and will add aDiscriminator
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 onStudentType
is justId
. The other two would both match onStudentTypeId
though, so as long as you aren't removing both of those, the conventions should pick it up.According to this question (Foreign key navigation property naming convention alternatives) though, you can also add
[ForeignKey("StudentTypeId")]
to theStudentType
property onStudent
and[InverseProperty("StudentType")]
to theStudents
property onStudentType
.希望有所帮助。 :)
这篇关于EF代码首次迁移:每层次错误的表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!