[C#.NET][Entity Framework] 觀察 Remove / RemoveRange Record 的行為
開發 SQL 時要養成開啟 SQL Profiler 的習慣,以便驗証執行結果是否如預期,這裡我將使用 Entity Framework 刪除資料
本文章節:
準備環境
1.北風資料庫
https://dotblogsfile.blob.core.windows.net/user/yc421206/1503/201531115108869.zip
2.範例會用到的三張表的關係圖
3.使用 Code First from Database [C#.NET][Entity Framework] Code First - Reverse engineering from an existing database at EF6.1.0
4.測試前資料準備
public static void Before(TestContext context)
{
using (NorthWindDbContext db = new NorthWindDbContext())
{
var detail11 = new Order_Detail()
{
OrderID = 11106,
ProductID = 1,
UnitPrice = 20.1m,
Quantity = 2,
Discount = 0
};
var detail12 = new Order_Detail()
{
OrderID = 11106,
ProductID = 2,
UnitPrice = 20.1m,
Quantity = 2,
Discount = 0
};
db.Order_Details.AddRange(new List<Order_Detail>()
{
detail11,
detail12,
});
try
{
db.SaveChanges();
}
catch (DbUpdateException ex)
{
throw;
}
}
}
先查詢再刪
情境:未知欲刪除的筆數
- 利用 Where 先找出實體,再刪除
- 調用 EF的 Remove | RemoveRange 會先送出 Sql Select 命令;若,EF 已經 ObjectStateManager 已經有資料,也就是調用過Linq立即執行的命令,比如 ToList()、Fisrt(),就不會送出 Sql Select
- 最後,調用 EF的 SaveChanges 這時才會送出 Sql Delete 命令
public void Delete_Test()
{
using (NorthWindDbContext db = new NorthWindDbContext())
{
var orderID = 11106;
var query = db.Order_Details.Where(o => o.OrderID == orderID);
db.Order_Details.RemoveRange(query);
db.SaveChanges();
}
}
EF 的 Where 並不會馬上送出 Select,而是會等到有立即執行的命令才會送出,比如 ToList、First
RemoveRange 會發出 Select 命令,如下圖:
這還蠻好理解,為了要動態兜 SQL Delete 的命令,所以跟 SQL Server 要了資料
SaveChanges 將刪除命令拆成三次執行,如下圖:
WHERE (([OrderID] = @0) AND ([ProductID] = @1))',N'@0 int,@1 int',@0=11106,@1=1
exec sp_executesql N'DELETE [dbo].[Order Details]
WHERE (([OrderID] = @0) AND ([ProductID] = @1))',N'@0 int,@1 int',@0=11106,@1=2
exec sp_executesql N'DELETE [dbo].[Order Details]
WHERE (([OrderID] = @0) AND ([ProductID] = @1))',N'@0 int,@1 int',@0=11106,@1=3
直接刪
情境:已知刪除的筆數
- 建立POCO,並指定PK,範例資料庫有 OrderID,ProductID 這兩個PK
- 設定 EntityState.Deleted
- 若資料不存在會跳出例外
public void EntityState_Delete_Test()
{
using (NorthWindDbContext db = new NorthWindDbContext())
{
var orderID = 11106;
var productIDs = new int[2] { 1, 2 };
foreach (var productId in productIDs)
{
var detail = new Order_Detail()
{
OrderID = orderID,
ProductID = productId
};
db.Order_Details.Attach(detail);
db.Entry(detail).State = EntityState.Deleted;
}
db.SaveChanges();
}
}
由下圖得知 SaveChanges 將刪除命令拆成兩次執行
WHERE (([OrderID] = @0) AND ([ProductID] = @1))',N'@0 int,@1 int',@0=11106,@1=1
exec sp_executesql N'DELETE [dbo].[Order Details]
WHERE (([OrderID] = @0) AND ([ProductID] = @1))',N'@0 int,@1 int',@0=11106,@1=2
命令被拆成兩次執行,需要使用交易嗎?
SaveChange 將 Delete 命令拆成兩次,我想要模擬刪除命令在某一筆資料被鎖定的情況之下是否還能順利的被刪除
下圖,中斷點停在 SaveChanges,且順利的執行 RemoveRange(會先送出 Select 命令)
下圖,設定其中一筆資料鎖定,模擬資料鎖定
下圖,命令雖然有下達,但失敗,例外跳出
最後,解除鎖定
整個模擬的動作,驗証 EF 的行為如我所預期,雖然有一筆命令送達,但因為發生例外了,刪除失敗。
2015/03/17 補充:感謝百敬老師協助
預設 Sql Profiler 錄不到 EF SaveChange 的 BEGIN TRAN,必須要自己勾選 Transactions
這時就可以看到 EF 傳給 SQL Server 的 BEGIN TRAN
當 EF 發生例外時,則使用 RollBack
結論:
原來 SaveChanges 已經有用到 Begin Tran
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET