我猜你在实体框架中使用继承时已经阅读了various strategies。实体框架的好处在于它隐藏了使用的继承策略。
由于链接中描述了三种继承策略,因此无需描述为此所需的fluent API。我只会写你最终会得到的类,以及如何对学生、教师和公共基类 Persons 进行查询。
此外,我将描述正确选择正确继承策略所需的一些注意事项。
所以你有Student 和Teachers,它们都派生自Person。
abstract class Person
{
public string Name {get; set}
public Gender Gender {get; set;}
public DateTime Birthday {get; set;}
}
public class Teacher : Person
{
public int Id {get; set;}
...
}
public class Student : Person
{
public int Id {get; set;}
...
}
还有 DbContext:
public class MyDbContext : DbContext
{
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Student> Students {get; set;}
}
现在,只要您有教师或学生的 IQueryable,您还可以使用 Person 的所有属性,而无需做任何特别的事情。
var maleTeachers = myDbContext.Teachers
.Where(teacher => teacher.Gender == Gender.Male);
var youngStudents = myDbcontext.Students
.Where(student => student.Birthday > new Datetime(2000, 1, 1);
如果您必须查询所有人员,则必须连接学生和教师。步步为营:
IQueryable<Person> teachers = myDbcontext.Teachers.Cast<Person>();
IQueryable<Person> students = myDbContext.Students.Cast<Person>();
IQueryable<Person> allPersons = teachers.Concat(students);
var result = allPersons.Where(person => ...)
.Select(person => ...)
... etc
当然这可以在一个语句中完成。
决定使用哪种继承策略
在决定继承策略时,请记住您最常执行的查询类型:
- 查询...的教师或查询...的学生
- 查询...的人员
如果您最常使用第一个,请考虑Table per concrete class (TPC). 您将有两张桌子,一张供学生使用,一张供教师使用。 Person 属性位于同一个表中。所以表Students会有Person列;表教师也将有人员列。
好处是,如果你问“Students who ...”,只涉及一张桌子。不需要加入。
缺点是,如果您要求“Persons that ...”,则需要将 Teachers 和 Students 表连接起来。
如果您要更频繁地查询 Persons,请考虑使用 creating Table per Type (TPT)。结果将是三个带有外键的表:Teachers、Persons、Student。在询问 Persons 时,只涉及一张桌子。但是,在请求教师时,我们总是需要连接两个表。
即使您选择了最佳继承策略,因为这些是您最常执行的查询类型,您有时也可能需要执行其他类型的查询。
TPC:每个具体类的表
如果您主要要求教师...或学生...您会选择此选项,您最终会得到两张表:一张用于学生,一张用于教师。不会有 Persons 表。
如果您必须偶尔执行 Person 查询,则必须连接两个序列。这是小步骤:
IQueryable<Person> teachers = myDbcontext.Teachers.Cast<Person>();
IQueryable<Person> students = myDbContext.Students.Cast<Person>();
IQueryable<Person> allPersons = teachers.Concat(students);
var result = allPersons.Where(person => ...)
.Select(person => ...)
... etc
当然这可以在一个语句中完成。
如果您必须更频繁地执行此操作,请考虑向您的 DbContext 类添加一个为您执行此操作的属性:
class MyDbcontext : Dbcontext
{
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Student> Students {get; set;}
public IQueryable<Person> Persons
{
get
{
return this.Teachers.Cast<Person>()
.Concat(this.Students.Cast<Person>());
}
}
}
用法如下:
using (var myDbContext = new MyDbContext(...))
{
IQueryable<Person> females = myDbcontext.Persons
.Where(person => person.Gender == Gender.Female);
}
如果您不想污染您的 Dbcontext,请考虑创建一个执行相同操作的扩展函数。见extension functions demystified
static class MyDbContextExtensions
{
IQueryable<Person> Persons(this MyDbContext dbContext)
{
return dbContext.Teachers.Cast<Person>()
.Concat(dbContext.Students.Cast<Person>());
}
}
TPT:每种类型的表格
您最终会得到三个表,Student、Teachers、Persons。学生和教师将有一个外键来识别他们是谁。
您可以使用 DbSet 直接查询 Persons。另一方面,如果您只想要...的教师,您只需访问 DbSet。只要您使用继承的 Person 属性之一,Entity 框架就会自动为您执行连接。您不会在代码中看到这一点。因此,即使您不进行连接,内部也会涉及两个表,这可能会降低效率。
所以要小心你选择的继承策略。