在基於 LINQ to SQL 的 N 層架構中,實體從 BLL 傳遞至 DAL 進行更新時,常面臨跨 DataContext 附加的問題。典型流程:
BLL.GetModel(id) → 修改屬性 → BLL.Update(entity) → DAL.Update(entity) → 成功
GetModel 返回後首個 DataContext 即釋放,DAL.Update 又新建一個 DataContext。LINQ to SQL 要求跨 Context 操作先 Attach,再 SubmitChanges。
直接 context.User.Attach(entity, true) 會拋出:
「已嘗試 Attach 或 Add 實體,該實體不是新實體,可能是從其他 DataContext 中加載來的……」
根因:實體關聯屬性(EntitySet<Message>、EntityRef<Image> 等)在查詢時未加載,但附加時這些屬性持有對舊 DataContext 的引用,導致 ObjectDisposedException。
在附加前,通過反射清空所有 EntitySet<>、EntityRef<> 字段/屬性,並移除 PropertyChanged/Changing 事件處理器:
public static void Detach<TEntity>(TEntity entity)
{
Type t = entity.GetType();
// 清空 EntitySet 屬性
foreach (var prop in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (prop.PropertyType.IsGenericType &&
prop.PropertyType.GetGenericTypeDefinition() == typeof(EntitySet<>))
prop.SetValue(entity, null, null);
}
// 清空 EntityRef 字段
foreach (var field in t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
if (field.FieldType.IsGenericType &&
field.FieldType.GetGenericTypeDefinition() == typeof(EntityRef<>))
field.SetValue(entity, null);
}
// 移除事件
var evtChanged = t.GetEvent("PropertyChanged");
var evtChanging = t.GetEvent("PropertyChanging");
if (evtChanged != null) evtChanged.RemoveEventHandler(entity, null);
if (evtChanging != null) evtChanging.RemoveEventHandler(entity, null);
}
GetModel 獲取實體Detach(entity) 斷開舊 Context 關聯context.Entity.Attach(entity, true) 附加到新 Contextcontext.SubmitChanges() 提交更新此法解決跨層傳遞實體的附加難題,保證多層架構下 LINQ to SQL 正常工作。