【Entity Framework】如何理解EF中的级联删除
一、概述
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。 但是,与在数据库中使用级联删除相比,这种环境要少得多。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |