那是因为您DbSet<User>
在 DbContext 而不是DbSet<Person>
. 当涉及到继承时, EF 代码首先具有可达性的概念,这基本上意味着如果您将一组基类放在上下文中,它将向下查找并包含其所有子类。
这里还有一个问题:您没有为您的Person
类指定主键,EF 也无法根据 Code First 约定推断出主键。默认情况下,EF 将模型中的继承映射到数据库中的 TPH(Type Per Hierarchy),并且子类从其基类继承键,该键已放在模型中的子类(即 UserId)中。
为了解决这个问题,我们首先将 DbContext 上的 DbSet 更改为 type Person
,然后我们需要向上移动UserId
到Person
基类并使其成为Key,如下所示:
public class Person
{
[Key]
public int UserId { get; set; }
public string Firstname { get; set; }
public string SurName { get; set; }
}
public class User : Person
{
public string CreatedOn { get; set; }
}
public class PersonDBContext : DbContext
{
public DbSet<Person> UserSet { get; set; }
}
该模型将产生以下 DB 模式,其中包含来自两个类的所有列(注意复数支持):
如何在 Code First 中将 TPH 更改为 TPT(按层次结构表):
就像我说的,按照惯例,EF 代码首先将使用 TPH 将继承映射到数据库并将其更改为 TPT,我们需要使用 Fluent API 覆盖约定:
public class StackOverflowContext : DbContext
{
public DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().MapHierarchy(p => new
{
p.UserId,
p.Firstname,
p.SurName,
})
.ToTable("Person");
modelBuilder.Entity<User>().MapHierarchy(u => new
{
u.UserId,
u.CreatedOn
})
.ToTable("User");
}
}
该模型将产生以下数据库模式:
如何在 Code First 中将 TPH 更改为 TPC(每个具体类型的表):
在每个具体类的表中,每个类都有一个表,每个表都有一个对应于该类型的每个属性的列。
代码如下所示,注意我关闭了主键属性的标识,因为两个表之间没有外键,我们需要注意提供唯一键。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().Property(p => p.UserId)
.StoreGeneratedPattern = System.Data.Metadata.Edm.StoreGeneratedPattern.None;
modelBuilder.Entity<Person>().MapSingleType(p => new
{
p.UserId,
p.Firstname,
p.SurName,
})
.ToTable("Person");
modelBuilder.Entity<User>().MapSingleType(u => new
{
u.UserId,
u.CreatedOn,
u.Firstname,
u.SurName,
})
.ToTable("User");
}
该模型将产生以下数据库模式: