ASP.NET/Web Service 等無狀態環境中,DataContext 存活週期短,實體物件需序列化傳輸、反序列化後 Attach 回新 Context,此過程極易觸發異常。
「物件的目前狀態使該作業無效。」
斷連模式下,實體未正確序列化導致關聯屬性內部狀態不一致。
SQLMetal 產生代碼時啟用單向序列化:
sqlmetal /conn:"..." /code:Entities.cs /map:Mapping.map /namespace:DMN /serialization:Unidirectional
Unidirectional 產生 [DataContract]、[DataMember] 屬性,支援 WCF/DataContractSerializer。
「物件圖包含循環,若停用參考追蹤則無法序列化。」
遞歸儲存時,父物件持有子集合,子物件反向參考父物件,形成雙向循環。
/serialization:Unidirectional (DataContractSerializer 自動處理循環)[DataMember(EmitDefaultValue=false)] 忽略反向導航屬性「若實體宣告了版本成員或沒有更新檢查策略,則只能將它附加為沒有原始狀態的已修改實體。」
Attach(entity) 時,實體有主鍵但映射檔無 IsVersion 或 UpdateCheck 設定,LINQ to SQL 無法判斷是否需樂觀鎖。
在 .map 檔主鍵欄位加上 IsVersion="true":
<Column Name="ID" Member="ID" Storage="_ID" DbType="Int NOT NULL IDENTITY"
IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" IsVersion="true" />
或在 DBML 設計工具將主鍵 Auto Generated Value 設為 True。
「未將物件參考設定為物件的實例。」
var q = from s in students
select new
{
s.Name,
ClassNO = s.Class.NO // 若學生未分班,s.Class 為 null → 拋出異常
};
// 方案一:三元運算子防禦
ClassNO = s.Class == null ? "" : s.Class.NO
// 方案二:C# 6.0 Null 條件運算子
ClassNO = s.Class?.NO ?? ""
// 方案三:Left Join 寫法 (轉為 SQL 執行)
var q = from s in ctx.Students
join c in ctx.Classes on s.ClassID equals c.ID into gj
from c in gj.DefaultIfEmpty()
select new { s.Name, ClassNO = c.NO ?? "" };
| 異常類型 | 核心原因 | 關鍵修正 |
|---|---|---|
| ForeignKeyReferenceAlreadyHasValueException | 實體未序列化/反序列化破壞關聯狀態 | /serialization:Unidirectional |
| SerializationException (循環) | 雙向導航屬性形成物件圖循環 | DataContractSerializer + Unidirectional / DTO |
| InvalidOperationException (版本) | Attach 時缺乏樂觀鎖策略 | 主鍵 IsVersion=true |
| NullReferenceException | 查詢投影中直接存取 Null 導航屬性 | Null 條件運算子 / Left Join |