溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何進行Entity?Framework?Core關(guān)聯(lián)刪除

發(fā)布時間:2021-12-29 19:39:46 來源:億速云 閱讀:190 作者:柒染 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān)如何進行Entity Framework Core關(guān)聯(lián)刪除,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

關(guān)聯(lián)刪除通常是一個數(shù)據(jù)庫術(shù)語,用于描述在刪除行時允許自動觸發(fā)刪除關(guān)聯(lián)行的特征;即當(dāng)主表的數(shù)據(jù)行被刪除時,自動將關(guān)聯(lián)表中依賴的數(shù)據(jù)行進行刪除,或者將外鍵更新為NULL或默認(rèn)值。

數(shù)據(jù)庫關(guān)聯(lián)刪除行為

我們先來看一看SQL Server中支持的行為。在創(chuàng)建外鍵約束時,可以指定關(guān)聯(lián)表在主表刪除行時,對依賴的數(shù)據(jù)如何執(zhí)行操作。例如下面的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,表示當(dāng)主表的數(shù)據(jù)行刪除時,自動將關(guān)聯(lián)表數(shù)據(jù)行的外鍵更新為NULL。

在SQL Server中支持如下四種行為:

1.ON DELETE NO ACTION

默認(rèn)行為,刪除主表數(shù)據(jù)行時,依賴表中的數(shù)據(jù)不會執(zhí)行任何操作,此時會產(chǎn)生錯誤,并回滾DELETE語句。例如會產(chǎn)生下面的錯誤:

DELETE 語句與 REFERENCE 約束"FK_Order Details_Orders_OrderID"沖突。該沖突發(fā)生于數(shù)據(jù)庫"Northwind_Test",表"dbo.Order Details", column 'OrderID'。

語句已終止。

2.ON DELETE CASCADE

刪除主表數(shù)據(jù)行時,依賴表的中數(shù)據(jù)行也會同步刪除。

3.ON DELETE SET NULL

刪除主表數(shù)據(jù)行時,將依賴表中數(shù)據(jù)行的外鍵更新為NULL。為了滿足此約束,目標(biāo)表的外鍵列必須可為空值。

4.ON DELETE SET DEFAULT

刪除主表數(shù)據(jù)行時,將依賴表的中數(shù)據(jù)行的外鍵更新為默認(rèn)值。為了滿足此約束,目標(biāo)表的所有外鍵列必須具有默認(rèn)值定義;如果外鍵可為空值,并且未顯式設(shè)置默認(rèn)值,則將使用NULL作為該列的隱式默認(rèn)值。

簡單介紹了數(shù)據(jù)庫中行為后,我們來著重介紹 EF Core 中的關(guān)聯(lián)實體的行為。

定義實體

我們先定義兩個實體Order、OrderDetail分別表示訂單和訂單明細(xì);其中Order與OrderDetail的關(guān)系是一對多,在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 配置關(guān)聯(lián)實體

在DbContext中OnModelCreating方法中,我們使用 Fluent API 配置實體中之間的關(guān)系。

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方法中,需要傳遞參數(shù)DeleteBehavior枚舉,分別有如下四個值:

public enum DeleteBehavior
    {
        Cascade,

        SetNull,

        ClientSetNull,

        Restrict
    }

這四個枚舉值的分別表示不同的行為,這也是我們今天的重點。

創(chuàng)建表結(jié)構(gòu)

我們分別使用使用這這個枚舉值,來創(chuàng)建數(shù)據(jù)表結(jié)構(gòu)。

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

四個枚舉值創(chuàng)建表的SQL語句類似如下,唯一區(qū)別在于創(chuàng)建外鍵約束[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
);

四個枚舉值分別對應(yīng)的SQL語句如下:

如何進行Entity?Framework?Core關(guān)聯(lián)刪除

EF Core 關(guān)聯(lián)實體刪除行為

我們分別通過枚舉值與是否跟蹤關(guān)聯(lián)實體,進行代碼測試,測試代碼如下:

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

如何進行Entity?Framework?Core關(guān)聯(lián)刪除        

總結(jié)

根據(jù)上面的測試結(jié)果,我們可以出得如下結(jié)論:

DeleteBehavior.Cascade

  • 如果關(guān)聯(lián)實體未被跟蹤,主實體的狀態(tài)標(biāo)記為刪除,執(zhí)行SaveChage時,在刪除主表的數(shù)據(jù)的同時,通過數(shù)據(jù)庫的行為刪除關(guān)聯(lián)表的數(shù)據(jù)行;

  • 如果關(guān)聯(lián)實體已經(jīng)被跟蹤,將主實體的狀態(tài)標(biāo)記為刪除時,關(guān)聯(lián)實體的狀態(tài)也會標(biāo)記為刪除,執(zhí)行SaveChange時,先刪除關(guān)聯(lián)表的數(shù)據(jù)行,然后再刪除主表的數(shù)據(jù)行;

  • 外鍵可以設(shè)置非空值、也可以設(shè)置為可為空值;

  • 關(guān)聯(lián)實體可以不被跟蹤。

DeleteBehavior.SetNull

  • 如果關(guān)聯(lián)實體未被跟蹤,主實體的狀態(tài)標(biāo)記為刪除,執(zhí)行SaveChage時,在刪除主表的數(shù)據(jù)時,通過數(shù)據(jù)庫的行為將關(guān)聯(lián)表數(shù)據(jù)行的外鍵更新為NULL,;

  • 如果關(guān)聯(lián)實體已經(jīng)被跟蹤,將主實體的狀態(tài)標(biāo)記為刪除時,關(guān)聯(lián)實體的外鍵會被設(shè)置為null,同時將關(guān)聯(lián)實體的狀態(tài)標(biāo)記為修改,執(zhí)行SaveChange時,先更新關(guān)聯(lián)表的數(shù)據(jù)行 ,然后刪除主表的數(shù)據(jù)行;

  • 因為要將外鍵更新為NULL,所以外鍵必須設(shè)置為可空字段;

  • 關(guān)聯(lián)實體可以不被跟蹤。

DeleteBehavior.ClientSetNull

  • 數(shù)據(jù)庫不會執(zhí)行任何行為;

  • 關(guān)聯(lián)實體必須被跟蹤,將主實體的狀態(tài)標(biāo)記為刪除時,關(guān)聯(lián)實體的外鍵被設(shè)置為null,同時將關(guān)聯(lián)實體的狀態(tài)標(biāo)記為修改,執(zhí)行SaveChange時,先更新關(guān)聯(lián)表的數(shù)據(jù)行,然后刪除主表的數(shù)據(jù)行(此時的行為與DeleteBehavior.SetNull一致);

  • 因為要將外鍵更新為NULL,所以外鍵必須設(shè)置為可空字段;

  • 關(guān)聯(lián)實體必須被跟蹤,否則保存數(shù)據(jù)時會拋出異常。

DeleteBehavior.Restrict

  • 框架不執(zhí)行任何操作,由開發(fā)人員決定關(guān)聯(lián)實體的行為,可以將關(guān)聯(lián)實體的狀態(tài)設(shè)置為刪除,也可以將關(guān)聯(lián)實體的外鍵設(shè)置為null;

  • 因為要修改關(guān)聯(lián)實體的狀態(tài)或外鍵的值,所以關(guān)聯(lián)實體必須被跟蹤。

關(guān)于如何進行Entity Framework Core關(guān)聯(lián)刪除就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI