【Entity Framework】如何理解EF中的級聯刪除
文章目錄
- 【Entity Framework】如何理解EF中的級聯刪除
- 一、概述
- 二、發生級聯行為時
- 2.1/刪除主體/父實體
- 2.2/斷開關系
- 三、發生級聯行為的位置
- 3.1/級聯刪除被跟蹤實體
- 3.2/數據庫中的級聯刪除
- 四、級聯NULL
一、概述
Entity Framework Core(EF Core)
表示使用外鍵的關系。具有外鍵的實體是關系中的子實體或依賴實體。此實體的外鍵值必須與相關主體/父實體的主鍵值(或替換鍵值)匹配。
如果刪除主體/父實體,則依賴項/子項的外鍵值將不再匹配任何主體/父實體的主鍵或替換鍵。 這是無效狀態,將導致在大多數數據庫中出現引用約束沖突。
可通過兩種方法來避免此引用約束沖突:
- 將外鍵值設置為 null。
- 同時刪除依賴實體/子實體
第一個選項僅適用于其中外鍵屬性(及其映射到的數據庫列)必須可為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。 但是,與在數據庫中使用級聯刪除相比,這種情況要少得多。