首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

LINQ to SQL 刪除操作全解析:級聯刪除、Identity Cache 陷阱與衝突處理

     編輯:關於.NET

承接《查詢與新增》、《更新》兩文,本文完整剖析 LINQ to SQL 刪除操作的核心行為:基本刪除、Identity Cache 導致的「刪除後仍可查詢」現象、關聯實體級聯刪除邏輯差異、以及並行衝突處理。

基本單筆刪除


var ctx = new NorthwindDataContext();
var test1 = ctx.Customers.Single(c => c.CustomerID == "TEST1");
ctx.Customers.DeleteOnSubmit(test1);
ctx.SubmitChanges(); // 發送 DELETE 陳述式

資料庫中該筆記錄確實被移除。

Identity Cache 陷阱:刪除後同一 DataContext 仍可查詢


var ctx = new NorthwindDataContext();
var test1 = ctx.Customers.Single(c => c.CustomerID == "TEST1");
ctx.Customers.DeleteOnSubmit(test1);
ctx.SubmitChanges(); // DB 已刪除

// 同一 DataContext 再次查詢
var again = ctx.Customers.Single(c => c.CustomerID == "TEST1");
Console.WriteLine(again.CustomerID); // 輸出 "TEST1" —— 似乎沒刪除!

原因

  • 資料庫記錄已刪除
  • 但 DataContext **Identity Cache 仍保留實體參考**
  • 主鍵查詢命中快取,直接回傳記憶體中已標記為「已刪除」的實體

驗證方式

  • 新建另一個 DataContext 查詢 → 找不到資料 (拋出 InvalidOperationException Single 失敗)
  • 檢查實體 entity.GetType() 是否為代理類別、或檢查 ctx.GetChangeSet().Deletes 確認刪除狀態
⚠️ 實務中若刪除後需立即驗證,**請使用新 DataContext 或關閉 ObjectTrackingEnabled**。

關聯實體刪除:Remove vs DeleteOnSubmit 的關鍵差異

場景:Customer (1) —< Order (*),欲刪除某客戶特定訂單。

錯誤做法:僅從集合移除


var order1 = test1.Orders.Single(o => o.ShipCity == "Shanghai");
test1.Orders.Remove(order1); // 僅移除關聯
ctx.SubmitChanges();

SQL Profiler 顯示:發送 UPDATE Orders SET CustomerID = NULL WHERE OrderID = ... —— **只是斷開關聯,未真正刪除 Order 記錄!**

正確做法:同時呼叫 DataContext.DeleteOnSubmit


var order1 = test1.Orders.Single(o => o.ShipCity == "Shanghai");
test1.Orders.Remove(order1);      // 維護物件圖一致性
ctx.Orders.DeleteOnSubmit(order1); // 真正標記為刪除
ctx.SubmitChanges();              // 發送 DELETE FROM Orders WHERE OrderID = ...

完整級聯刪除:刪除客戶及其所有訂單

選項 A:應用層手動級聯 (不依賴資料庫 FK Cascade)


var ctx = new NorthwindDataContext();
var test1 = ctx.Customers.Single(c => c.CustomerID == "TEST1");

foreach (var o in test1.Orders.ToList()) // ToList 避免迭代中修改集合
{
    test1.Orders.Remove(o);
    ctx.Orders.DeleteOnSubmit(o);
}
ctx.Customers.DeleteOnSubmit(test1);
ctx.SubmitChanges();

選項 B:資料庫層級 ON DELETE CASCADE (推薦)


-- SQL Server 設定外鍵級聯刪除
ALTER TABLE Orders
ADD CONSTRAINT FK_Orders_Customers
FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
ON DELETE CASCADE;
  • 僅需 ctx.Customers.DeleteOnSubmit(customer); ctx.SubmitChanges();
  • 資料庫自動刪除關聯 Orders,效能最佳、邏輯最簡潔
  • LINQ to SQL 實體端無需額外代碼,**但需注意**:實體物件圖中 customer.Orders 集合不會自動清空,後續存取可能引發例外,建議刪除後捨棄該 DataContext。

並行衝突處理 (與更新機制相同)

刪除時若資料已被他人修改/刪除,SubmitChanges() 拋出 ChangeConflictException。處理模式:


try { ctx.SubmitChanges(); }
catch (ChangeConflictException)
{
    foreach (var conflict in ctx.ChangeConflicts)
    {
        // KeepCurrentValues: 強制刪除 (忽略並行衝突)
        // OverwriteCurrentValues: 放棄刪除 (重新載入資料庫值)
        conflict.Resolve(RefreshMode.KeepCurrentValues);
    }
    ctx.SubmitChanges(); // 重試
}
刪除操作通常選 KeepCurrentValues (強制刪除) 或 OverwriteCurrentValues (放棄刪除),視業務語意決定。

總結:刪除操作決策樹

  1. **單筆獨立實體** → DeleteOnSubmit + SubmitChanges
  2. **關聯子實體** → Parent.Children.Remove(child) + ctx.Children.DeleteOnSubmit(child) (雙重呼叫)
  3. **父實體含子集合** → 推薦資料庫 ON DELETE CASCADE,應用層僅刪父實體
  4. **大量刪除** → ctx.ExecuteCommand("DELETE ...") (參考《大量刪除效能優化》)
  5. **刪除後驗證** → 新 DataContext 或關閉追蹤

掌握 Remove (維護物件圖) 與 DeleteOnSubmit (標記資料庫刪除) 的職責分離,是正確使用 LINQ to SQL 刪除操作的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved