关系简介

本文档简单介绍了对象模型和关系数据库中的关系表示形式,包括 EF Core 在两者之间的映射方式。

对象模型中的关系

关系定义两个实体之间的相关性。 例如,在博客中对文章建模时,每个文章都与其发布的博客相关,并且博客与该博客上发布的所有文章相关。

在面向对象的语言(如 C#)中,博客和文章通常由两个类表示:BlogPost。 例如:

public class Blog
{
    public string Name { get; set; }
    public virtual Uri SiteUri { get; set; }
}
public class Post
{
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime PublishedOn { get; set; }
    public bool Archived { get; set; }
}

在上面的类中,没有任何指示 BlogPost 相关的内容。 可将其添加到对象模型中,方法是向发布它的 Blog 添加来自 Post 的引用:

public class Post
{
    public string Title { get; set; }
    public string Content { get; set; }
    public DateOnly PublishedOn { get; set; }
    public bool Archived { get; set; }

    public Blog Blog { get; set; }
}

同样,同一关系的相反方向可以表示为每个 BlogPost 对象的集合:

public class Blog
{
    public string Name { get; set; }
    public virtual Uri SiteUri { get; set; }

    public ICollection<Post> Posts { get; }
}

BlogPost 的连接,以及相反的从 Post 回到 Blog 的连接被称为 EF Core 中的“关系”。

重要

单一关系通常可以按任一方向遍历。 在此示例中,即通过 Blog.Posts 属性从 BlogPost,以及通过 Post.Blog 属性从 Post 返回到 Blog。 这是一个关系,不是两个。

提示

在 EF Core 中,Blog.PostsPost.Blog 属性称为“导航”。

关系数据库中的关系

关系数据库使用外键表示关系。 例如,使用 SQL Server 或 Azure SQL 时,可以使用下表来表示我们的 PostBlog 类:

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NULL,
    [Content] nvarchar(max) NULL,
    [PublishedOn] datetime2 NOT NULL,
    [Archived] bit NOT NULL,
    [BlogId] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]) ON DELETE CASCADE);

CREATE TABLE [Blogs] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    [SiteUri] nvarchar(max) NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY ([Id]));

在此关系模型中,PostsBlogs 表各被分配了一个“主键”列。 主键的值能够唯一地标识每个文章或博客。 此外,Posts 表还被分配了一个“外键”列。 Blogs 主键列 IdPosts 表的 BlogId 外键列引用。 此列是“受约束的”,即 PostsBlogId 列中的任何值必须BlogsId 列中的某个值匹配。 此匹配方式可确定每个文章与哪个博客相关。 例如,如果 Posts 表的一行中的 BlogId 值为 7,则由该行表示的文章发布于带有主键 7 的博客中。

EF Core 中的映射关系

EF Core 关系映射就是将关系数据库中使用的主键/外键表示形式映射到对象模型中使用的对象之间的引用。

从最基本的意义上讲,这涉及到:

  • 将主键属性添加到每个实体类型。
  • 将外键属性添加到一个实体类型。
  • 将实体类型之间的引用与主键和外键相关联,以形成单一关系配置。

完成此映射后,EF 会在对象之间的引用更改时根据需要更改外键值,并在外键值更改时根据需要更改对象之间的引用。

注意

主键用于多个映射关系。 有关详细信息,请参阅

例如,上面所示的实体类型可以通过主键和外键属性进行更新:

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Uri SiteUri { get; set; }

    public ICollection<Post> Posts { get; }
}
public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime PublishedOn { get; set; }
    public bool Archived { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

提示

主键和外键属性不需要是实体类型的公开可见属性。 但是,即使属性处于隐藏状态,也务必要确定它们仍存在于 EF 模型中。

然后,Blog 的主键属性 Blog.IdPost 的外键属性 Post.BlogId 可以与实体类型(Blog.PostsPost.Blog)之间的引用(“导航”)相关联。 在生成类似这样的简单关系时,EF 会自动执行此操作,但在重写你的 DbContextOnModelCreating 方法时也可以显式指定。 例如:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .HasPrincipalKey(e => e.Id);
}

现在,所有这些属性将一起作为 BlogPost 之间的单一关系的表示形式产生一致的行为。

了解更多

EF 支持多种不同类型的关系,可通过多种不同的方式表示和配置这些关系。 要跳转到不同类型的关系的示例,请参阅:

如果你不熟悉 EF,那么试用在上面的项目符号中链接的示例是了解关系的工作原理的好方法。

要深入了解关系映射中涉及的实体类型的属性,请参阅:

EF 模型是使用三种机制的组合构建的:约定、映射属性和模型生成器 API。 大多数示例都显示了模型生成 API。 要了解有关其他选项的详细信息,请参阅:

  • 关系约定:关于发现实体类型、其属性以及类型之间的关系。
  • 关系映射属性:对于关系配置的某些方面,可用作模型生成 API 的替代。

重要

模型生成 API 是 EF 模型的最终真相来源–它始终优先于约定发现的配置,或映射属性指定的配置。 它也是配置 EF 模型的各个方面的唯一完全保真的机制。

与关系相关的其他主题包括:

  • 级联删除:介绍在调用 SaveChangesSaveChangesAsync 时,如何自动删除相关实体。
  • 从属实体类型使用特殊类型的“从属”关系,它意味着两种类型之间的连接比此处讨论的“正常”关系更紧密。 此处为正常关系介绍的许多概念都适用于从属关系。 然而,从属关系也有其自己的特殊行为。

提示

阅读文档时,根据需要参阅关系术语词汇表,以帮助了解所使用的术语。

使用关系

模型中定义的关系可以通过多种方式使用。 例如: