继承¶
EF 可以将 .NET 类型层次结构映射到数据库。
这使你可以像平常一样使用基类型和派生类型在代码中编写 .NET 实体,并让
EF 无缝创建适当的数据库架构、发出查询等。
目前,EF Core 仅支持每个层次结构一个表(TPH)模式。 TPH
使用单个表来存储层次结构中所有类型的数据,而鉴别器列用于标识每行所表示的类型。
实体类型层次结构映射¶
按照约定,EF
不会自动扫描基类型或派生类型;这意味着,如果要映射层次结构中的 CLR
类型,则必须在模型上显式指定该类型。
例如,仅指定层次结构的基类型将不会导致 EF Core 隐式包含其所有子类型。
下面的示例为及其子类公开了 DbSet Blog RssBlog 。 如果 Blog
有任何其他子类,则不会将其包含在模型中。
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<RssBlog> RssBlogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class RssBlog : Blog
{
public string RssUrl { get; set; }
}
此模型映射到下面的数据库架构(请注意隐式创建的鉴别器列,该列标识每个行中存储的博客类型):
| BlogId | Discriminator | Url | RssUrl |
|---|---|---|---|
| 1 | Blog | https://docs.microsoft.com | NULL |
| 2 | RssBlog | https://docs.microsoft.com | https://docs.microsoft.com |
CREATE TABLE Blogs (BlogId INT PRIMARY KEY NOT NULL,Discriminator VARCHAR(64) NOT NULL,Url VARCHAR(128) NOT NULL,RssUrl VARCHAR(128));
鉴别器配置¶
您可以配置鉴别器列的名称(默认Discriminator)和类型以及用于标识层次结构中的每种类型的值:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasDiscriminator<string>("blog_type")
.HasValue<Blog>("blog_base")
.HasValue<RssBlog>("blog_rss");
}
在上述示例中,EF 在层次结构的基实体上将鉴别器隐式添加为阴影属性。 此属性可以配置为类似于任何其他属性:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property("Discriminator")
.HasMaxLength(128);
}
最后,鉴别器还可以映射到实体中的常规 .NET 属性:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasDiscriminator(b => b.BlogType);
modelBuilder.Entity<Blog>()
.Property(e => e.BlogType)
.HasMaxLength(200)
.HasColumnName("blog_type");
}
共享列¶
默认情况下,当层次结构中的两个兄弟实体类型具有相同名称的属性时,它们将映射到两个单独的列。 但是,如果它们的类型相同,它们可以映射到同一个数据库列:
public class MyContext : DbContext
{
public DbSet<BlogBase> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.HasColumnName("Url");
modelBuilder.Entity<RssBlog>()
.Property(b => b.Url)
.HasColumnName("Url");
}
}
public abstract class BlogBase
{
public int BlogId { get; set; }
}
public class Blog : BlogBase
{
public string Url { get; set; }
}
public class RssBlog : BlogBase
{
public string Url { get; set; }
}