2012-06-14 239 views
1

我很難更新實體框架中的實體。使用實體框架4.3更新實體 - 代碼優先

場景: - 我加載新的DbContext()GetById(GUID)一個實體 - 我嘗試使用擴展方法來保存這個實體,那麼使用新的DbContext()

我的繼承人更新方法:

public virtual void Update(IEntity entityToUpdate) 
    { 
     var dbEntry = Context.Entry(entityToUpdate); 
     if (dbEntry == null) return; 

     if (Context.Entry(entityToUpdate).State == EntityState.Detached) 
      DbSet.Attach(entityToUpdate); 
     else 
     { 
      dbEntry.CurrentValues.SetValues(entityToUpdate); 
      Context.Entry(entityToUpdate).State = EntityState.Modified; 
     } 

     Context.SaveChanges(); 
    } 

這是我的一個嘗試的集合。如果我使用SetValues,我被告知該實體被分離並因此不可能,並且如果使用附加,我會得到以下錯誤: 'ObjectStateManager中已存在具有相同鍵的對象。 ObjectStateManager不能使用同一個鍵跟蹤多個對象。'

我明顯在做一些根本性的錯誤。有人可以幫助我在正確的方向嗎?

UPDATE:

protected void TransferClubs(object sender, EventArgs e) 
    { 
     var clubHelper = new ClubHelper(); 
     var club = clubHelper.GetClub(new Guid("A009D0CD-71C4-42E8-88E2-037F059B12EE")); 
     club.AddUser(Guid.NewGuid(), ClubRoleType.Admin); 
     club.AddUser(Guid.NewGuid(), ClubRoleType.Admin); 

     club.Save(); 
    } 

    public static bool Save(this ClubItem item) 
    { 
     var clubHelper = new ClubHelper(); 
     clubHelper.AddOrUpdate(item); 
     return true; 
    } 

    public ClubItem AddOrUpdate(ClubItem item) 
    { 
     if (item.Id == Guid.Empty) 
      Insert(item); 
     else 
      Update(item); 

     return item; 
    } 

而你在我原來的職位看Update()方法...

+0

你能給出一個加載實體,改變它然後調用這個Update方法的完整上下文的例子嗎?你說更新發生在一個「新的DbContext()」中,但是你有一個關於已經存在的(=附加的)具有相同鍵的對象的例外。我無法想象這會發生在一個*新的*上下文中。 – Slauma

+0

新增所有玩法。添加用戶將新用戶添加到ICollection 集合 – Kulvis

+0

'ClubHelper'創建一個新的上下文,'GetClub'從DB加載一個現有的俱樂部,'AddUser'應該創建一個新用戶,將其插入到數據庫並設置一個參考去俱樂部吧? – Slauma

回答

2

在我看來,如果你在TransferClubs最後一行club.Save();

更換更新應該工作
clubHelper.SaveChanges(); 

此方法應該簡單地調用Context.SaveChanges();並將更改保存到加載的實體,即創建兩個新的用戶與一個forei gn鍵設置爲加載的club。老實說,你更新實體的方法相當奇怪和複雜。我不知道爲什麼你的代碼拋出一個「ObjectStateManager中已經存在一個具有相同鍵的對象,ObjectStateManager不能使用相同的鍵」異常跟蹤多個對象。但有幾個缺陷和事情是沒有意義的:

  • 你沒有處理你的實例化上下文。如果ClubHelper創建一個新的上下文,它應該在它超出範圍時進行處理。因此,ClubHelper應該實施IDisposableDispose實施應調用context.Dispose()。然後你可以使用一個using,在結束時自動部署實例化對象:

    protected void TransferClubs(object sender, EventArgs e) 
    { 
        using (var clubHelper = new ClubHelper()) 
        { 
         // stuff... 
        } // Dispose called here automatically 
    } 
    
  • 這些線是沒有意義的:

    var dbEntry = Context.Entry(entityToUpdate); 
    if (dbEntry == null) return; 
    

    如果entityToUpdate是你的對象模型的實體​​是從來沒有null。它只能有國家Detached。如果entityToUpdate不是您的對象模型的實體Entry會拋出異常但不返回null

  • if情況下沒有意義:

    if (Context.Entry(entityToUpdate).State == EntityState.Detached) 
        DbSet.Attach(entityToUpdate); 
    // ... 
    Context.SaveChanges(); 
    

    Attach狀態Unchanged增加了一個實體的上下文。如果您在此之後僅調用SaveChanges,則什麼都不會發生,並且將被寫入數據庫,因爲沒有任何更改。

  • 另外,else情況下沒有意義:

    dbEntry.CurrentValues.SetValues(entityToUpdate); 
    Context.Entry(entityToUpdate).State = EntityState.Modified; 
    

    SetValues副本entityToUpdate的屬性具有相同的密鑰,並已附加到上下文如果屬性有實體的屬性相同的名字。如果屬性值不同,則將該屬性標記爲Modified。如果您之後將整個實體設置爲Modified,則將全部屬性標記爲Modified,這使得SetValues在冗餘之前。

    此外,這兩行不會幫助您創建所需的UPDATE語句,因爲它們不會將關係標記爲已修改並且僅影響標量(和複雜)屬性,而不會影響導航屬性。但更新關係 - 即club與兩位新用戶之間的關係 - 正是您在示例中需要的。

  • 最後它沒有意義的,在一個完全新的上下文進行更新...

    var clubHelper = new ClubHelper(); 
    clubHelper.AddOrUpdate(item); 
    

    ...當你已經加載,並且在另一個上下文之前只是在線條改變了實體。實體框架已經完成了所有工作,以跟蹤第一個上下文中的更改以生成所需的SQL語句。如果在此之後創建一個新的上下文,您希望在其中保存更改,則將所有工作都拋棄,並且必須從頭開始,以告知EF您對原始實體所做的更改。

+0

我粘貼的代碼是從解決方案中的不同位置收集的,並未向您提供我所有的需求,對此感到抱歉。有些地方同樣的clubhelper不可用,然後我需要擴展方法。正如我在我的文章中提到的那樣,Update方法是我的一些嘗試來掌握問題的集合:)應該在粘貼之前清理它。 我仍然有這個問題,但我嘗試改變我的clubHelper用法,看看我是否可以簡化這一點。非常感謝非常詳細和澄清的答案:) – Kulvis

+0

@Kulvis:我誤解了一下。我批評那種形式並不存在的代碼,對不起!希望答案在所有方面都很有用...... – Slauma

+0

你在批評代碼時是正確的,現在我們試圖讓它更簡單。我們仍然需要擴展方法,但我們沒有得到「..key exists ...」錯誤。新問題是ClubUsers集合中的noe實體被保存到數據庫中。在附加俱樂部後,他們在上下文中沒有被發現爲新的。我將clubitem的狀態更改爲已修改,但是我是否也必須對每個ClubUser對象執行此操作? – Kulvis