如果你要描述一个Student,并且你必须描述Student.StudentId和Student.PersonId的含义,你能给出一个恰当的描述吗?
在我看来,你想给学生一个新的主键属性的唯一原因是因为这个属性的标识符,而不是因为真正的正确含义。
在实体框架中建模继承时应使用的方法取决于您最常使用的查询类型。
假设您有三种类型的人:学生、教师和家长。考虑一下您最常做的查询类型:
- 您最常询问有...的学生,还是...的家长?
- 或者,您是否会经常针对...的人,很少针对...的学生或...的老师
(1) 主要要求学生...:使用 TPC
如果您将继承策略建模为Table per Concrete Class (TPC),那么学生、教师和家长将各自拥有自己的表。没有共同的父母表。学生拥有的所有父属性都在学生表中,类似地,所有这些父属性也在教师表中。
如果您主要要求学生...而很少要求...询问...的人总是需要对...的学生和...的老师以及...的父母的查询结果串联
我发现这是我最常用的继承策略。
(2) 主要要求...的人:使用 TPT
如果这些是最常用的查询,最好将所有人员数据建模在一个单独的表中,让学生、教师和家长都可以参考他们的人员数据:Table-Per-Type (TPT)
如何实现 TPC
如果按照TPC来实现继承,那么就不会有单独的Person表了。无需给 Persons 一个 ID。
class Person
{
public string FirstName {get; set;}
...
}
class Student : Person
{
public int Id {get; set;}
...
}
class Teacher : Person
{
public int Id{get; set;
...
}
如果你打算从不实例化一个人,只有学生、老师和家长,才能声明人的类抽象
顺便说一句,如果你给主键属性默认名称Id,实体框架会知道这应该是你的主键。不需要属性,也不需要流畅的 API。
在您的 DbContext 中:
class MyDbContext : DbContext
{
public DbSet<Student> Students {get; set;}
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Parent> Parents {get; set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().Map(student =>
{
student.MapInheritedProperties();
student.ToTable(nameof(MyDbContext.Students));
});
modelBuilder.Entity<Teacher>().Map(teacher =>
{
teacher.MapInheritedProperties();
teacher.ToTable(nameof(MyDbContext.Teachers));
});
// etc. for Parent
}
}
如何实施 TPT
会有一个 Persons 表,Student 和 Teachers 将引用 Persons 表。
class Person
{
public int Id {get;set;}
...
}
class Student
{
public int Id {get;set;}
// A student has Person data via foreign key:
public int PersonId {get; set;}
public Person Person {get; set;}
...
}
class Teacher
{
public int Id {get;set;}
// A Teacher has Person data via foreign key:
public int PersonId {get; set;}
public Person Person {get; set;}
...
}