網站首頁 編程語言 正文
關聯刪除通常是一個數據庫術語,用于描述在刪除行時允許自動觸發刪除關聯行的特征;即當主表的數據行被刪除時,自動將關聯表中依賴的數據行進行刪除,或者將外鍵更新為NULL或默認值。
數據庫關聯刪除行為
我們先來看一看SQL Server中支持的行為。在創建外鍵約束時,可以指定關聯表在主表刪除行時,對依賴的數據如何執行操作。例如下面的SQL語句,[Order Details]表中[OrderID]字段 是外鍵,依賴于[Orders]表中的主鍵[OrderID]。
CREATE TABLE [Orders] ( [OrderID] int NOT NULL IDENTITY, [Name] nvarchar(max) NULL, [OrderDate] datetime2 NULL, CONSTRAINT [PK_Orders] PRIMARY KEY ([OrderID]) ); GO CREATE TABLE [Order Details] ( [DetailId] int NOT NULL IDENTITY, [OrderID] int NULL, [ProductID] int NOT NULL, CONSTRAINT [PK_Order Details] PRIMARY KEY ([DetailId]), CONSTRAINT [FK_Order Details_Orders_OrderID] FOREIGN KEY ([OrderID]) REFERENCES [Orders] ([OrderID]) ON DELETE SET NULL );
外鍵約束[FK_Order Details_Orders_OrderID]末尾的語句是ON DELETE SET NULL,表示當主表的數據行刪除時,自動將關聯表數據行的外鍵更新為NULL。
在SQL Server中支持如下四種行為:
1.ON DELETE NO ACTION
默認行為,刪除主表數據行時,依賴表中的數據不會執行任何操作,此時會產生錯誤,并回滾DELETE語句。例如會產生下面的錯誤:
DELETE 語句與 REFERENCE 約束"FK_Order Details_Orders_OrderID"沖突。該沖突發生于數據庫"Northwind_Test",表"dbo.Order Details", column 'OrderID'。
語句已終止。
2.ON DELETE CASCADE
刪除主表數據行時,依賴表的中數據行也會同步刪除。
3.ON DELETE SET NULL
刪除主表數據行時,將依賴表中數據行的外鍵更新為NULL。為了滿足此約束,目標表的外鍵列必須可為空值。
4.ON DELETE SET DEFAULT
刪除主表數據行時,將依賴表的中數據行的外鍵更新為默認值。為了滿足此約束,目標表的所有外鍵列必須具有默認值定義;如果外鍵可為空值,并且未顯式設置默認值,則將使用NULL作為該列的隱式默認值。
簡單介紹了數據庫中行為后,我們來著重介紹 EF Core 中的關聯實體的行為。
定義實體
我們先定義兩個實體Order、OrderDetail分別表示訂單和訂單明細;其中Order與OrderDetail的關系是一對多,在OrderDetail實體中OrderID表示外鍵,依賴于Order實體中的主鍵OrderID。
public class Order { public int OrderID { get; set; } public string Name { get; set; } public DateTime? OrderDate { get; set; } public ICollection<OrderDetail> OrderDetails { get; set; } } public class OrderDetail { public int DetailId { get; set; } public int? OrderID { get; set; } public int ProductID { get; set; } public Order Order { get; set; } }
Fluent API 配置關聯實體
在DbContext中OnModelCreating方法中,我們使用 Fluent API 配置實體中之間的關系。
public class NorthwindContext : DbContext { public virtual DbSet<Order> Orders { get; set; } public virtual DbSet<OrderDetail> OrderDetails { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Order>( builder => { builder.HasMany<OrderDetail>(e => e.OrderDetails).WithOne(e => e.Order).HasForeignKey(e => e.OrderID).OnDelete(DeleteBehavior.ClientSetNull); }); } }
在OnDelete方法中,需要傳遞參數DeleteBehavior枚舉,分別有如下四個值:
public enum DeleteBehavior { Cascade, SetNull, ClientSetNull, Restrict }
這四個枚舉值的分別表示不同的行為,這也是我們今天的重點。
創建表結構
我們分別使用使用這這個枚舉值,來創建數據表結構。
[InlineData(DeleteBehavior.Cascade)] [InlineData(DeleteBehavior.SetNull)] [InlineData(DeleteBehavior.ClientSetNull)] [InlineData(DeleteBehavior.Restrict)] [Theory] public void Create_Database(DeleteBehavior behavior) { using (var northwindContext = new NorthwindContext(behavior)) { northwindContext.Database.EnsureDeleted(); northwindContext.Database.EnsureCreated(); } }
四個枚舉值創建表的SQL語句類似如下,唯一區別在于創建外鍵約束[FK_Order Details_Orders_OrderID]中ON DELETE {}后面的語句。
CREATE TABLE [Orders] ( [OrderID] int NOT NULL IDENTITY, [Name] nvarchar(max) NULL, [OrderDate] datetime2 NULL, CONSTRAINT [PK_Orders] PRIMARY KEY ([OrderID]) ); GO CREATE TABLE [Order Details] ( [DetailId] int NOT NULL IDENTITY, [OrderID] int NOT NULL, [ProductID] int NOT NULL, CONSTRAINT [PK_Order Details] PRIMARY KEY ([DetailId]), CONSTRAINT [FK_Order Details_Orders_OrderID] FOREIGN KEY ([OrderID]) REFERENCES [Orders] ([OrderID]) ON DELETE CASCADE );
四個枚舉值分別對應的SQL語句如下:
EF Core 關聯實體刪除行為
我們分別通過枚舉值與是否跟蹤關聯實體,進行代碼測試,測試代碼如下:
[InlineData(DeleteBehavior.Cascade, true)] [InlineData(DeleteBehavior.Cascade, false)] [InlineData(DeleteBehavior.SetNull, true)] [InlineData(DeleteBehavior.SetNull, false)] [InlineData(DeleteBehavior.ClientSetNull, true)] [InlineData(DeleteBehavior.ClientSetNull, false)] [InlineData(DeleteBehavior.Restrict, true)] [InlineData(DeleteBehavior.Restrict, false)] [Theory] public void Execute(DeleteBehavior behavior, bool includeDetail) { using (var northwindContext = new NorthwindContext(behavior)) { northwindContext.Database.EnsureDeleted(); northwindContext.Database.EnsureCreated(); } int orderId; int detailId; using (var northwindContext = new NorthwindContext(behavior)) { var order = new Order { Name = "Order1" }; var orderDetail = new OrderDetail { ProductID = 11 }; order.OrderDetails = new List<OrderDetail> { orderDetail }; northwindContext.Set<Order>().Add(order); northwindContext.SaveChanges(); orderId = order.OrderID; detailId = orderDetail.DetailId; } using (var northwindContext = new NorthwindContext(behavior)) { var queryable = northwindContext.Set<Order>().Where(e => e.OrderID == orderId); if (includeDetail){ queryable = queryable.Include(e => e.OrderDetails); } var order = queryable.Single(); northwindContext.Set<Order>().Remove(order); try { northwindContext.SaveChanges(); DumpSql(); } catch (Exception) { DumpSql(); throw; } } using (var northwindContext = new NorthwindContext(behavior)) { var orderDetail = northwindContext.Set<OrderDetail>().Find(detailId); if (behavior == DeleteBehavior.Cascade) { Assert.Null(orderDetail); } else { Assert.NotNull(orderDetail); } } } ?
? ? ? ??
總結
根據上面的測試結果,我們可以出得如下結論:
DeleteBehavior.Cascade
- 如果關聯實體未被跟蹤,主實體的狀態標記為刪除,執行SaveChage時,在刪除主表的數據的同時,通過數據庫的行為刪除關聯表的數據行;
- 如果關聯實體已經被跟蹤,將主實體的狀態標記為刪除時,關聯實體的狀態也會標記為刪除,執行SaveChange時,先刪除關聯表的數據行,然后再刪除主表的數據行;
- 外鍵可以設置非空值、也可以設置為可為空值;
- 關聯實體可以不被跟蹤。
DeleteBehavior.SetNull
- 如果關聯實體未被跟蹤,主實體的狀態標記為刪除,執行SaveChage時,在刪除主表的數據時,通過數據庫的行為將關聯表數據行的外鍵更新為NULL,;
- 如果關聯實體已經被跟蹤,將主實體的狀態標記為刪除時,關聯實體的外鍵會被設置為null,同時將關聯實體的狀態標記為修改,執行SaveChange時,先更新關聯表的數據行 ,然后刪除主表的數據行;
- 因為要將外鍵更新為NULL,所以外鍵必須設置為可空字段;
- 關聯實體可以不被跟蹤。
DeleteBehavior.ClientSetNull
- 數據庫不會執行任何行為;
- 關聯實體必須被跟蹤,將主實體的狀態標記為刪除時,關聯實體的外鍵被設置為null,同時將關聯實體的狀態標記為修改,執行SaveChange時,先更新關聯表的數據行,然后刪除主表的數據行(此時的行為與DeleteBehavior.SetNull一致);
- 因為要將外鍵更新為NULL,所以外鍵必須設置為可空字段;
- 關聯實體必須被跟蹤,否則保存數據時會拋出異常。
DeleteBehavior.Restrict
- 框架不執行任何操作,由開發人員決定關聯實體的行為,可以將關聯實體的狀態設置為刪除,也可以將關聯實體的外鍵設置為null;
- 因為要修改關聯實體的狀態或外鍵的值,所以關聯實體必須被跟蹤。?
原文鏈接:https://www.cnblogs.com/tdfblog/p/entity-framework-core-cascade-delete.html
相關推薦
- 2022-07-24 Redis中ServiceStack.Redis和StackExchange.Redis區別詳解_R
- 2022-05-19 Nginx+Windows搭建域名訪問環境的操作方法_nginx
- 2022-12-14 C語言程序設計之指針的應用詳解_C 語言
- 2022-04-18 ASP.Net?Core?MVC基礎系列之獲取配置信息_基礎應用
- 2022-07-04 C#實現中文日歷Calendar_C#教程
- 2022-04-12 C++中的異常實例詳解_C 語言
- 2023-10-12 v-if和v-for的優先級以及二者同時使用的情況
- 2023-06-03 C++11學習之右值引用和移動語義詳解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支