注意:
1.本文中所提到的“實體”均為由LINQ TO SQL生成的(即.dbml)
2.你需要了解LINQ TO SQL對表關聯的實現方式,EntitySet 和 EntityRef
也許你看到標題後,會覺得問題比較抽象,那麼我舉個實例來具體說明一下問題。
在基於LINQ TO SQL的N層架構中,假如我們需要對一個實體進行更新,那麼流程應是這樣:
流程
BLL.GetModel(p=>p.id==1) --> 修改相應屬性(字段)值 --> BLL.Update(Entity entity) --> DAL.Update(Entity entity) --> 更新成功
此時,我們需要將這個實體從業務層(BLL)傳遞到數據訪問層(DAL),當GetModel方法返回實體後 會立即釋放掉DataContext,然後到了執行DAL.Update(Entity entity)方法時又重新實例化一個 DataContext來進行更新操作;可見被傳遞的實體是從第一個DataContext傳遞到另一個 DataContext,在 LINQ TO SQL中這種跨越不同DataContext操作時,需要先進行附加操作,也就是 context.Entity.Attach(entity,true); 最後再context.SubmitChanges();
還是看看代碼吧:
class BLL
{
private readonly DAL dal = new DAL();
public LinqToSqlProvider.User GetModel (Expression<Func<LinqToSqlProvider.User, bool>> expression)
{
dal.GetModel(expression);
}
public void Update(LinqToSqlProvider.User entity)
{
dal.Update(entity);
}
}
class DAL
{
public LinqToSqlProvider.User GetModel (Expression<Func<LinqToSqlProvider.User, bool>> expression)
{
LinqToSqlProvider.User entry = new CriTextBroadcast.LinqToSqlProvider.User();
using (CriTextBroadcastDBDataContext context = DataContext)
{
entry = context.User.SingleOrDefault(expression);
}
return entry;
}
public void Update(LinqToSqlProvider.User entity)
{
using (CriTextBroadcastDBDataContext context = DataContext)
{
context.User.Attach(entry, true);
context.SubmitChanges();
}
}
}
實際我們用以上代碼操作時,會出現此異常:
已嘗試 Attach 或 Add 實體,該實體不是新實體,可能是從其他 DataContext 中加載來 的......
查了N多資料未果,後來還是在一國外的帖子裡看到了類似問題。原來是這個實體對應的表有若干個關 聯表,如:User -> Message ,User -> Images等,在生成實體類時會自動產生: EntitySet<Message> Message這樣的屬性,由於我們在獲取實體時並未將這些關聯的內容一起讀出 來,而在附加時(Attach)這些屬性便會出現ObjectDisposedException異常,也就是因為之前查詢這個 實體時的DataContext已經釋放掉了導致的。
解決方法:
/// <summary>
/// 輔助LinqToSql實體與原DataContext分離
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
public static void Detatch<TEntity>(TEntity entity)
{
Type t = entity.GetType();
System.Reflection.PropertyInfo[] properties = t.GetProperties (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (var property in properties)
{
string name = property.Name;
if (property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(EntitySet<>))
{
property.SetValue(entity, null, null);
}
}
System.Reflection.FieldInfo[] fields = t.GetFields (System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (var field in fields)
{
string name = field.Name;
if (field.FieldType.IsGenericType &&
field.FieldType.GetGenericTypeDefinition() == typeof (EntityRef<>))
{
field.SetValue(entity, null);
}
}
System.Reflection.EventInfo eventPropertyChanged = t.GetEvent ("PropertyChanged");
System.Reflection.EventInfo eventPropertyChanging = t.GetEvent("PropertyChanging");
if (eventPropertyChanged != null)
{
eventPropertyChanged.RemoveEventHandler(entity, null);
}
if (eventPropertyChanging != null)
{
eventPropertyChanging.RemoveEventHandler(entity, null);
}
}
應在獲得實體後使用Detach(entity)方法將實體與原DataContext分離,然後再附加(Attach)到新的 DataContext中去