【Entity Framework】如何理解EF中的级联删除

简介: 【Entity Framework】如何理解EF中的级联删除

一、概述

Entity Framework Core(EF Core)表示使用外键的关系。具有外键的实体是关系中的子实体或依赖实体。此实体的外键值必须与相关主体/父实体的主键值(或替换键值)匹配。


如果删除主体/父实体,则依赖项/子项的外键值将不再匹配任何主体/父实体的主键或替换键。 这是无效状态,将导致在大多数数据库中出现引用约束冲突。


可通过两种方法来避免此引用约束冲突:

  1. 将外键值设置为 null。
  2. 同时删除依赖实体/子实体

第一个选项仅适用于其中外键属性(及其映射到的数据库列)必须可为null的可选关系。

第二个选项适用于任何类型的关系,它被称作"级联删除"。


二、发生级联行为时

当依赖实体/子实体无法再与其当前主体/父实体关联时,需要执行级联删除。发生这种情况的原因可能是主体/父实体已被删除。或者当主体/父实体仍存在,但依赖实体/子实体在再与其关联时。

2.1/删除主体/父实体

本文中使用的实体类

public class Blog
{
    public int Id{get;set;}
    public string NameP{get;set;}
    public IList<Post> Posts{get;}=new List<Post>();
}
public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

其中Blog是与Post(依赖实体/子实体)的关系中的主体/父实体。Post.BlogId是一个外键属性,其值必须与该文章所属博客中的Blog.Id主键匹配。


按照约定,由于Post.BlogId外键属性是不可为null的,因此该关系被配置为必需的。默认情况下,所需的关系配置为使用级联删除。

删除博客时,所有文章都将被级联删除。

using var context = new BlogsContext();
var blog = context.Blogs.OrderBy(e=>.Name).Include(e => e.Posts).First();
context.Remove(blog);
context.SaveChanges();

SaveChanges 以 SQL Server 为例,生成以下 SQL:

CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;

SELECT @@ROWCOUNT;
CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

2.2/断开关系

如果不删除博客,而是断开每篇文章与其博客之间的关系。为此,可将每篇文章的引用导航Post.Blog设置为null:

using var context= new BlogsContext();
var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();
foreach(var post in blog.Posts)
{
    post.Blog = null;
}
context.SaveChanges();

还可通过从 Blog.Posts 集合导航中删除每篇文章内容来断开关系:

using var context = new BlogsContext();
var blog = context.Blogs.OrderBy(e => e.Name).Include(e.Posts).First();
blog.Posts.Clear();
context.SaveChanges();

无论哪种情况,结果都一样:没有删除博客,但是删除了不再与任何博客关联的文章:

SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

删除不再与任何主体/依赖实体关联的实体这一行为被称作“删除孤立项”。


级联删除和删除孤立项时密切相关的。当断开与所需的主体/父实体之间的关系时,两者都将导致删除依赖实体/子实体。对于级联删除,由于主体/父实体本身已删除,因此发生了这种断开。对于孤立项,主体/父实体仍然存在,但不再与依赖实体/子实体相关。


三、发生级联行为的位置

可将级联行为应用于:

  • 当前DbContext跟踪的实体
  • 数据库中尚未加载到上下文中的实体

3.1/级联删除被跟踪实体

EF Core 始终将配置的级联行为应用于跟踪的实体。 这意味着如上面的示例所示,如果应用程序将所有相关的依赖实体/子实体加载到 DbContext 中,则无论如何配置数据库,都将正确应用级联行为。

3.2/数据库中的级联删除

许多数据库系统还提供在数据库中删除实体时触发的级联行为。使用EnsureCreated或EF Core迁移创建数据库时,EF Core会根据EF Core模型中的级联删除行为来配置这些行为。

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NULL,
    [Content] nvarchar(max) 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
);

请注意,定义博客和文章之间关系的外键约束是用 ON DELETE CASCADE 配置的。

如果我们知道数据库是这样配置的,那么我们可以删除博客,而无需先加载文章,数据库将负责删除与此博客相关的所有文章。 例如:

using var context = new BlogsContext();
var blog = context.Blogs.OrderBy(e => e.Name).First();
context.Remove(blog);
context.SaveChanges();


四、级联NULL

可选关系将可为 null 的外键属性映射到可为 null 的数据库列。 这意味着当删除当前主体/父实体或断开与依赖实体/子实体的关系时,可将外键值设置为 NULL。

让我们再看一下发生级联行为时的示例,但这次可选关系由可为null的Post.BlogId外键属性表示:

public int? BlogId { get; set; }

删除每篇文章的相关博客时,该文章的外键属性将设置为 NULL。 例如,此代码与之前的代码相同:

using var context = new BlogsContext();
var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();
context.Remove(blog);
context.SaveChanges();

当删除可选关系中的主体/父实体时,数据库也可配置为级联 NULL。 但是,与在数据库中使用级联删除相比,这种情况要少得多。

目录
相关文章
|
5月前
|
SQL 关系型数据库 数据库连接
详解 Entity Framework(EF)核心组件与数据访问方法探索
Entity Framework是一个ORM框架,简化.NET开发者与数据库的交互。它始于.NET Framework的一部分,但现在可通过NuGet独立获取。ORM允许对象模型直接映射到数据库结构,避免直接编写SQL。
296 2
详解 Entity Framework(EF)核心组件与数据访问方法探索
|
5月前
|
开发框架 缓存 .NET
【Entity Framework】EF中DbSet类详解
【Entity Framework】EF中DbSet类详解
85 1
【Entity Framework】EF中DbSet类详解
|
5月前
|
SQL 存储 开发框架
实体框架EF(Entity Framework)简介
实体框架EF(Entity Framework)简介
110 7
|
5月前
|
SQL 存储 开发框架
【Entity Framework】EF中的增删改查
【Entity Framework】EF中的增删改查
132 0
|
5月前
|
SQL 开发框架 .NET
【Entity Framework】聊一聊EF如何使用数据库函数
【Entity Framework】聊一聊EF如何使用数据库函数
67 0
|
5月前
|
存储 SQL 开发框架
【Entity Framework】聊一聊EF中继承关系
【Entity Framework】聊一聊EF中继承关系
38 0
|
5月前
|
存储 SQL 开发框架
【Entity Framework】如何使用EF中的生成值
【Entity Framework】如何使用EF中的生成值
36 0
|
5月前
|
开发框架 .NET 数据库
【Entity Framework】EF中SaveChanges如何使用
【Entity Framework】EF中SaveChanges如何使用
38 0
|
5月前
|
存储 SQL API
【Entity Framework】EF中实体属性
【Entity Framework】EF中实体属性
34 0
|
5月前
|
SQL 开发框架 .NET
【Entity Framework】聊聊EF中复杂查询运算符
【Entity Framework】聊聊EF中复杂查询运算符
98 0