在下面的示例中,CTP仅考虑User类属性,但我希望在Users表中包含基类属性。
有没有办法告诉CTP包含基类属性?或者什么可以替代解决方案?
class PersonDBContext : DbContext
{
public DbSet<User> UserSet { get; set; }
}
class Person
{
public string Firstname { get; set; }
public string SurName { get; set; }
}
class User : Person
{
public int UserId { get; set; }
public string CreatedOn { get; set; }
}
答案 0 :(得分:3)
那是因为您在DbContext而不是DbSet<User>
中声明了DbSet<Person>
。当涉及到继承时,EF代码首先具有 reachability 的概念,这基本上意味着如果你在上下文中放置一组基类,它将向下发现并包含它的所有子类。
此处还有一个问题:您没有为Person
类指定主键,EF也无法根据Code First约定推断出一个主键。默认情况下,EF将模型中的继承映射到数据库中的TPH(每个层次结构类型),并且子类从其基类继承该键,该基类已放入模型中的子类(即UserId)中。
要解决此问题,我们首先将DbContext上的DbSet更改为Person
类型,然后我们需要将UserId
向上移动到Person
基类并使其成为键如下:
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; }
}
此模型将生成以下数据库模式,其中包含两个类中的所有列(请注意复数支持):
就像我说的那样,按照惯例,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");
}
}
此模型将产生以下数据库架构:
在每个具体类的表中,每个类都有一个表,每个表都有一个列,用于该类型的每个属性。
此代码如下所示,请注意我在主键属性上关闭了标识,因为两个表之间没有外键,我们需要处理提供唯一键。
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");
}
此模型将生成以下数据库模式: