Entity Framework Core 關聯刪除

来源:https://www.cnblogs.com/tdfblog/archive/2018/05/28/entity-framework-core-cascade-delete.html
-Advertisement-
Play Games

關聯刪除通常是一個資料庫術語,用於描述在刪除行時允許自動觸發刪除關聯行的特征;即當主表的數據行被刪除時,自動將關聯表中依賴的數據行進行刪除,或者將外鍵更新為 或預設值。 資料庫關聯刪除行為 我們先來看一看SQL Server中支持的行為。在創建外鍵約束時,可以指定關聯表在主表刪除行時,對依賴的數據如 ...


關聯刪除通常是一個資料庫術語,用於描述在刪除行時允許自動觸發刪除關聯行的特征;即當主表的數據行被刪除時,自動將關聯表中依賴的數據行進行刪除,或者將外鍵更新為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中支持如下四種行為:

  • ON DELETE NO ACTION
    預設行為,刪除主表數據行時,依賴表中的數據不會執行任何操作,此時會產生錯誤,並回滾DELETE語句。例如會產生下麵的錯誤:

    DELETE 語句與 REFERENCE 約束"FK_Order Details_Orders_OrderID"衝突。該衝突發生於資料庫"Northwind_Test",表"dbo.Order Details", column 'OrderID'。
    語句已終止。

  • ON DELETE CASCADE
    刪除主表數據行時,依賴表的中數據行也會同步刪除。
  • ON DELETE SET NULL
    刪除主表數據行時,將依賴表中數據行的外鍵更新為NULL。為了滿足此約束,目標表的外鍵列必須可為空值。
  • ON DELETE SET DEFAULT
    刪除主表數據行時,將依賴表的中數據行的外鍵更新為預設值。為了滿足此約束,目標表的所有外鍵列必須具有預設值定義;如果外鍵可為空值,並且未顯式設置預設值,則將使用NULL作為該列的隱式預設值。

簡單介紹了資料庫中行為後,我們來著重介紹 EF Core 中的關聯實體的行為。

定義實體

我們先定義兩個實體OrderOrderDetail分別表示訂單和訂單明細;其中OrderOrderDetail的關係是一對多,在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 配置關聯實體

DbContextOnModelCreating方法中,我們使用 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語句如下:

枚舉值 SQL語句
DeleteBehavior.Cascade ON DELETE CASCADE
DeleteBehavior.SetNull ON DELETE SET NULL
DeleteBehavior.ClientSetNull ON DELETE NO ACTION
DeleteBehavior.Restrict ON DELETE NO ACTION

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);
                }
            }
        }
        
枚舉值 是否跟蹤關聯實體 是否成功調用SaveChange 關聯實體是否存在 執行的SQL
DeleteBehavior.Cascade No 成功 DELETE FROM [Orders] WHERE [OrderID] = 1
DeleteBehavior.Cascade YES 成功 DELETE FROM [Order Details] WHERE [DetailId] = 1
DELETE FROM [Orders] WHERE [OrderID] = 1
DeleteBehavior.SetNull No 成功 YES
(外鍵為NULL
DELETE FROM [Orders] WHERE [OrderID] = 1
DeleteBehavior.SetNull YES 成功 YES
(外鍵為NULL
UPDATE [Order Details] SET [OrderID] = NULL WHERE [DetailId] = 1
DELETE FROM [Orders] WHERE [OrderID] = 1
DeleteBehavior.ClientSetNull No 失敗
(外鍵約束)
YES DELETE FROM [Orders] WHERE [OrderID] = 1
DeleteBehavior.ClientSetNull YES 成功 YES
(外鍵為NULL
UPDATE [Order Details] SET [OrderID] = NULL WHERE [DetailId] = 1
DELETE FROM [Orders] WHERE [OrderID] = 1
DeleteBehavior.Restrict No 失敗
(外鍵約束)
YES DELETE FROM [Orders] WHERE [OrderID] = 1
DeleteBehavior.Restrict YES 失敗
(外鍵約束)
YES DELETE FROM [Orders] WHERE [OrderID] = 1

總結

根據上面的測試結果,我們可以出得如下結論:

DeleteBehavior.Cascade

  • 如果關聯實體未被跟蹤,主實體的狀態標記為刪除,執行SaveChage時,在刪除主表的數據的同時,通過資料庫的行為刪除關聯表的數據行;
  • 如果關聯實體已經被跟蹤,將主實體的狀態標記為刪除時,關聯實體的狀態也會標記為刪除,執行SaveChange時,先刪除關聯表的數據行,然後再刪除主表的數據行;
  • 外鍵可以設置非空值、也可以設置為可為空值;
  • 關聯實體可以不被跟蹤。

DeleteBehavior.SetNull

  • 如果關聯實體未被跟蹤,主實體的狀態標記為刪除,執行SaveChage時,在刪除主表的數據時,通過資料庫的行為將關聯表數據行的外鍵更新為NULL,;
  • 如果關聯實體已經被跟蹤,將主實體的狀態標記為刪除時,關聯實體的外鍵會被設置為null,同時將關聯實體的狀態標記為修改,執行SaveChange時,先更新關聯表的數據行 ,然後刪除主表的數據行;
  • 因為要將外鍵更新為NULL,所以外鍵必須設置為可空欄位;
  • 關聯實體可以不被跟蹤。

DeleteBehavior.ClientSetNull

  • 資料庫不會執行任何行為;
  • 關聯實體必須被跟蹤,將主實體的狀態標記為刪除時,關聯實體的外鍵被設置為null,同時將關聯實體的狀態標記為修改,執行SaveChange時,先更新關聯表的數據行,然後刪除主表的數據行(此時的行為與DeleteBehavior.SetNull一致);
  • 因為要將外鍵更新為NULL,所以外鍵必須設置為可空欄位;
  • 關聯實體必須被跟蹤,否則保存數據時會拋出異常。

DeleteBehavior.Restrict

  • 框架不執行任何操作,由開發人員決定關聯實體的行為,可以將關聯實體的狀態設置為刪除,也可以將關聯實體的外鍵設置為null
  • 因為要修改關聯實體的狀態或外鍵的值,所以關聯實體必須被跟蹤。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 簡介 抖音,是一款可以拍短視頻的音樂創意短視頻社交軟體,該軟體於2016年9月上線,是一個專註年輕人的15秒音樂短視頻社區。用戶可以通過這款軟體選擇歌曲,拍攝15秒的音樂短視頻,形成自己的作品。此APP已在Android各大應用商店和APP Store均有上線。 今天咱們就用Python爬取抖音視頻 ...
  • 一.列表及元組 1.首先我們先來看一下列表: 列表是我們最常用的數據類型之一,通過列表可以對數據實現最方便的存儲、修改等操作 創建類表的兩種方式: (1.)l1=[1,2,3,4] (2.)l1=list((1,2,3,4)) (1.)和(2.)是等價的都是創建列表的方式 列表常用的方法: 我們可以 ...
  • 題目: 給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。 設計一個演算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。 註意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。 示例 1: 輸入: [7,1,5,3,6,4] 輸出: 7 解釋 ...
  • 一:Hash結構集合 Hash結構的特點是無序和唯一,無序即添加元素的順序和輸出元素的順序不一致,唯一是指元素不重覆。那是什麼來保證Hash結構元素唯一的呢? 元素所在類的HashCode()和equals()方法來保證元素的唯一性的,所以自定義的類用Hash結構集合存儲元素時,需要重寫這兩個方法。 ...
  • 登錄百度 先清理瀏覽器緩存,打開Charles,登錄一次百度主頁,抓取到登錄過程。 參數分析 確定了需要分析的參數,從哪個開始分析呢?隨意吧 一般有些參數之間是有關係的,比如token的請求參數里需要gid參數 這裡我就不一一去分析參數間的關係了,直接來了啊 參數 gid 一方面其他參數需要它,另外 ...
  • 資料庫就是存儲數據的倉庫,其本質是一個文件系統,數據按照特定的格式將數據存儲起來,用戶可以對資料庫中的數據進行增加,修改,刪除及查詢操作。 mysql的dos視窗啟動關閉命令:net start mysql和net stop mysql 登錄命令:(1)mysql -u用戶名 -p密碼 (2)mys ...
  • 我根據自己的理解,對原文的精華部分進行了提煉,併在一些難以理解的地方加上了自己的“可能比較準確”的「翻譯」。 Chapter4 設計與聲明 Designs and Declarations 條款18: 讓介面容易被正確使用,不易被誤用 欲開發一個“容易被使用,不容易被誤用”的介面,首先必須考慮客戶可 ...
  • 環境配置:windows ,VS,SQLite(點擊下載),System.Data.SQLite.DLL(點擊下載)。 目錄: 一、新建項目,添加引用 二、創建資料庫 三、創建表 四、插入數據 五、查詢數據 一、新建項目,添加引用 1.在VS中新建一個控制台應用程式,如下圖 2.添加引用 將下載的S ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...