1. 程式人生 > 程式設計 >Entity Framework Core關聯刪除

Entity Framework Core關聯刪除

目錄
  • 關聯刪除行為
  • 定義實體
  • Fluent API 配置關聯實體
  • 建立表結構
  • EF Core 關聯實體刪除行為
  • 總結
    • DeleteBehavior.Cascade
    • DeleteBehavior.SetNull
    • DeleteBehavior.ClientSetNull
    • DeleteBehavior.Restrict

關聯刪除通常是一個數據庫術語,用於描述在刪除行時允許自動觸發刪除關聯行的特徵;即當主表的資料行被刪除時,自動將關聯表中依賴的資料行進行刪除,或者將外來鍵更新為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).HasForeiwww.cppcns.comgnKey(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,[OrderID] int NOT NULL,CONSTRAINT [FK_Order Details_Orders_OrderID] FOREIGN KEY ([OrderID]) REFERENCES [Orders] ([OrderID]) ON DELETE CASCADE
);

四個列舉值分別對應的SQL語句如下:

EntityFrameworkCore關聯刪除

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);
          GtSZHpMu      }

                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);
                }
            }
        }
        

EntityFrameworkCore關聯刪除

總結

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

DeleteBehavior.Cascade

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

DeleteBehavior.SetNull

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

DeleteBehavior.ClientSetNull

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

DeleteBehavior.Restrict

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

以上就是Entity Framework Core關聯刪除的詳細內容,更多關於Entity Framework關聯刪除的資料請關注我們其它相關文章!