Entity?Framework?Core關(guān)聯(lián)刪除
關(guān)聯(lián)刪除通常是一個數(shù)據(jù)庫術(shù)語,用于描述在刪除行時允許自動觸發(fā)刪除關(guān)聯(lián)行的特征;即當主表的數(shù)據(jù)行被刪除時,自動將關(guān)聯(lián)表中依賴的數(shù)據(jù)行進行刪除,或者將外鍵更新為NULL或默認值。
數(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,表示當主表的數(shù)據(jù)行刪除時,自動將關(guān)聯(lián)表數(shù)據(jù)行的外鍵更新為NULL。
在SQL Server中支持如下四種行為:
1.ON DELETE NO ACTION
默認行為,刪除主表數(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。為了滿足此約束,目標表的外鍵列必須可為空值。
4.ON DELETE SET DEFAULT
刪除主表數(shù)據(jù)行時,將依賴表的中數(shù)據(jù)行的外鍵更新為默認值。為了滿足此約束,目標表的所有外鍵列必須具有默認值定義;如果外鍵可為空值,并且未顯式設(shè)置默認值,則將使用NULL作為該列的隱式默認值。
簡單介紹了數(shù)據(jù)庫中行為后,我們來著重介紹 EF Core 中的關(guān)聯(lián)實體的行為。
定義實體
我們先定義兩個實體Order、OrderDetail分別表示訂單和訂單明細;其中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語句如下:
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); } } } ?
? ? ? ??
總結(jié)
根據(jù)上面的測試結(jié)果,我們可以出得如下結(jié)論:
DeleteBehavior.Cascade
- 如果關(guān)聯(lián)實體未被跟蹤,主實體的狀態(tài)標記為刪除,執(zhí)行SaveChage時,在刪除主表的數(shù)據(jù)的同時,通過數(shù)據(jù)庫的行為刪除關(guān)聯(lián)表的數(shù)據(jù)行;
- 如果關(guān)聯(lián)實體已經(jīng)被跟蹤,將主實體的狀態(tài)標記為刪除時,關(guān)聯(lián)實體的狀態(tài)也會標記為刪除,執(zhí)行SaveChange時,先刪除關(guān)聯(lián)表的數(shù)據(jù)行,然后再刪除主表的數(shù)據(jù)行;
- 外鍵可以設(shè)置非空值、也可以設(shè)置為可為空值;
- 關(guān)聯(lián)實體可以不被跟蹤。
DeleteBehavior.SetNull
- 如果關(guān)聯(lián)實體未被跟蹤,主實體的狀態(tài)標記為刪除,執(zhí)行SaveChage時,在刪除主表的數(shù)據(jù)時,通過數(shù)據(jù)庫的行為將關(guān)聯(lián)表數(shù)據(jù)行的外鍵更新為NULL,;
- 如果關(guān)聯(lián)實體已經(jīng)被跟蹤,將主實體的狀態(tài)標記為刪除時,關(guān)聯(lián)實體的外鍵會被設(shè)置為null,同時將關(guān)聯(lián)實體的狀態(tài)標記為修改,執(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)標記為刪除時,關(guān)聯(lián)實體的外鍵被設(shè)置為null,同時將關(guān)聯(lián)實體的狀態(tài)標記為修改,執(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)實體必須被跟蹤。?
以上就是Entity Framework Core關(guān)聯(lián)刪除的詳細內(nèi)容,更多關(guān)于Entity Framework關(guān)聯(lián)刪除的資料請關(guān)注腳本之家其它相關(guān)文章!
- Entity Framework Core使用控制臺程序生成數(shù)據(jù)庫表
- Entity Framework Core延遲加載(懶加載)用法
- Entity?Framework?Core實現(xiàn)Like查詢詳解
- Entity Framework Core中執(zhí)行SQL語句和存儲過程的方法介紹
- Entity Framework Core批處理SQL語句
- Entity Framework Core實現(xiàn)軟刪除與查詢過濾器
- Entity Framework Core生成列并跟蹤列記錄
- ASP.NET Core在WebApi項目中使用MiniProfiler分析Entity Framework Core
- Entity Framework Core工具使用命令行
- 詳解如何在ASP.NET Core中應(yīng)用Entity Framework
- Entity Framework Core對Web項目生成數(shù)據(jù)庫表
相關(guān)文章
.net WINFORM的GDI雙緩沖的實現(xiàn)方法
下面小編就為大家分享一篇.net WINFORM的GDI雙緩沖的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12asp.net中強制取消TFS2008中其它成員的簽出文件的方法
有個項目,以前的成員離職了,剛好又簽出了一個文件在TFS中并且上了鎖,導(dǎo)致后面的維護無法簽入和生成。在網(wǎng)上查了一下,找到了如下解決辦法2012-08-08在Asp.net core中實現(xiàn)websocket通信
這篇文章介紹了在Asp.net core中實現(xiàn)websocket通信的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07.net core高吞吐遠程方法如何調(diào)用組件XRPC詳解
這篇文章主要給大家介紹了關(guān)于.net core高吞吐遠程方法如何調(diào)用組件XRPC的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用.net core具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05sqlserver 刪除重復(fù)記錄處理(轉(zhuǎn))
刪除重復(fù)記錄有大小關(guān)系時,保留大或小其中一個記錄2011-07-07asp.net GridView控件鼠標移動某行改變背景顏色(方法一)
asp.net GridView控件鼠標移動某行改變背景顏色2009-12-12asp.net頁面與頁面之間傳參數(shù)值方法(post傳值和get傳值)
這篇文章主要介紹了asp.net頁面與頁面之間傳參數(shù)值方法,說明了post傳值和get傳值的使用方法,需要的朋友可以參考下2014-02-02利用ASP.NET MVC和Bootstrap快速搭建個人博客之后臺dataTable數(shù)據(jù)列表
jQuery dataTables 插件是一個優(yōu)秀的表格插件,應(yīng)用非常廣泛,本文給大家介紹利用ASP.NET MVC和Bootstrap快速搭建個人博客之后臺dataTable數(shù)據(jù)列表,非常不錯,具有參考借鑒價值,感興趣的朋友一起看下吧2016-07-07