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

LINQ to SQL 更新數據庫(上):Attach 陷阱與異常全景圖

     編輯:關於.NET

LINQ to SQL 在斷開環境(N-Tier)下更新實體,Attach 方法充滿陷阱。本文從最簡單場景層層遞進,複現所有異常,並通過反編譯源碼揭示根因,為後續方案鋪墊。

從簡單開始:UI 直連無問題

var db = new NorthwindDataContext();
var p = db.Products.Single(x => x.ID == 1);
p.Name = "Changed";
db.SubmitChanges(); // 成功

分層災難:Repository 模式引發異常

// Get 隔離上下文
public Product GetProduct(int id) {
    using (var db = new NorthwindDataContext())
        return db.Products.SingleOrDefault(p => p.ID == id);
}

// Update 不同上下文
public void UpdateProduct(Product p) {
    using (var db = new NorthwindDataContext()) {
        db.Products.Attach(p); // 默認 Attach(entity, false)
        db.SubmitChanges();
    }
}

異常 1:NotSupportedException - 實體非新且來自其他上下文

原因:實體含關聯屬性(Category、Supplier 等),觸發延遲加載器,HasDeferredLoaders=true,StandardChangeTracker.Track 拋出異常。

嘗試修復:Attach(entity, true) 強制修改態

異常 2:InvalidOperationException - 無版本成員無法附加為修改態

LINQ to SQL 預設樂觀並發檢查需 RowVersion(timestamp 列)或 IsVersion=true。無版本成員時,Attach(entity, true) 被禁止。

添加 Timestamp 列後:關聯實體再現異常 1

即使主表有 Timestamp,關聯實體(Category)無 Timestamp 仍觸發異常 1。

嘗試 Attach(entity, originalEntity) 重載

依然異常 1,因關聯實體延遲加載器未斷開。

源碼深度揭秘

異常 1 根因:HasDeferredLoaders

if (trackedObject.HasDeferredLoaders)
    throw Error.CannotAttachAddNonNewEntities();

HasDeferredLoaders 遍歷所有 Association 與 Deferred 成員,檢查是否有延遲加載器。只要實體存在任何關聯導航屬性,Attach 即失敗。

異常 2 根因:版本成員檢查

if (asModified && (versionMember == null) && inheritanceType.HasUpdateCheck)
    throw Error.CannotAttachAsModifiedWithoutOriginalState();

HasUpdateCheck 檢查是否有 UpdateCheck != Never 的列。預設所有列 Always,故必須有版本列。

Attach 方法 12 種組合可用性表

僅 第 7 情況:有 RowVersion 且無關聯 時 Attach(entity, true) 正常工作。這在實際系統中幾乎不可能。

核心啟示

LINQ to SQL 設計初衷側重連線場景(UI 直連),斷開更新並非一等公民。Repository 模式需自行解決 Attach 限制,下篇將給出方案。

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