Asp.net-Mvc

保存 EF4 POCO 對象的更改時更新關係

  • September 3, 2010

實體框架 4、POCO 對象和 ASP.Net MVC2。我有一個多對多的關係,比如說 BlogPost 和 Tag 實體之間。這意味著在我的 T4 生成的 POCO BlogPost 類中,我有:

public virtual ICollection<Tag> Tags {
   // getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;

我從 ObjectContext 的一個實例中請求一個 BlogPost 和相關的標籤,並將其發送到另一個層(MVC 應用程序中的視圖)。稍後我取回更新後的 BlogPost,其中包含更改的屬性和更改的關係。例如,它有標籤“A”、“B”和“C”,新標籤是“C”和“D”。在我的特定範例中,沒有新的標籤,並且標籤的屬性永遠不會改變,所以唯一應該保存的是改變的關係。現在我需要將它保存在另一個 ObjectContext 中。(更新:現在我嘗試在同一個上下文實例中做,但也失敗了。)

問題:我不能讓它正確地保存關係。我嘗試了我發現的一切:

  • Controller.UpdateModel 和 Controller.TryUpdateModel 不起作用。
  • 從上下文中獲取舊的 BlogPost 然後修改集合不起作用。(與下一點不同的方法)
  • 可能會起作用,但我希望這只是一種解決方法,而不是解決方案:(。
  • 在所有可能的組合中嘗試了 BlogPost 和/或標籤的 Attach/Add/ChangeObjectState 函式。失敗的。
  • 看起來像我需要的,但它不起作用(我試圖修復它,但不能解決我的問題)。
  • 試過ChangeState/Add/Attach/…上下文的關係對象。失敗的。

“不起作用”意味著在大多數情況下,我在給定的“解決方案”上工作,直到它不產生錯誤並至少保存 BlogPost 的屬性。關係會發生什麼變化:通常標籤會使用新的 PK 再次添加到標籤表中,並且保存的 BlogPost 引用這些而不是原始的。當然,返回的標籤有 PK,在保存/更新方法之前,我檢查了 PK,它們與數據庫中的相同,所以 EF 可能認為它們是新對象,而那些 PK 是臨時對象。

我知道並且可能無法找到自動化簡單解決方案的問題:當 POCO 對象的集合發生更改時,上述虛擬集合屬性應該會發生這種情況,因為 FixupCollection 技巧將更新另一端的反向引用的多對多關係。然而,當視圖“返回”一個更新的 BlogPost 對象時,這並沒有發生。這意味著我的問題可能沒有簡單的解決方案,但這會讓我非常難過,我會討厭 EF4-POCO-MVC 的勝利:(。這也意味著 EF 不能在 MVC 環境中做到這一點使用 EF4 對像類型:(。我認為基於快照的更改跟踪應該發現更改後的 BlogPost 與具有現有 PK 的標籤有關係。

順便說一句:我認為一對多關係也會出現同樣的問題(Google和我的同事都這麼說)。我會在家裡試一試,但即使這對我的應用程序中的六個多對多關係沒有幫助:(。

讓我們這樣嘗試:

  • 將 BlogPost 附加到上下文中。將對象附加到上下文後,對象的狀態、所有相關對象和所有關係都設置為未更改。
  • 使用 context.ObjectStateManager.ChangeObjectState 將您的 BlogPost 設置為 Modified
  • 遍歷標籤集合
  • 使用 context.ObjectStateManager.ChangeRelationshipState 設置目前 Tag 和 BlogPost 之間關係的狀態。
  • 保存更改

編輯:

我想我的一條評論給了你錯誤的希望,EF 會為你做合併。我在這個問題上玩了很多,我的結論是 EF 不會為你做這件事。我想你也在MSDN上找到了我的問題。事實上,網際網路上有很多這樣的問題。問題是沒有明確說明如何處理這種情況。那麼讓我們來看看這個問題:

問題背景

EF 需要跟踪實體的更改,以便持久性知道必須更新、插入或刪除哪些記錄。問題是 ObjectContext 負責跟踪更改。ObjectContext 只能跟踪附加實體的更改。在 ObjectContext 之外創建的實體根本不會被跟踪。

問題描述

基於上述描述,我們可以清楚地說明 EF 更適合實體始終附加到上下文的連接場景——典型的 WinForm 應用程序。Web 應用程序需要斷開連接的場景,其中上下文在請求處理後關閉,實體內容作為 HTTP 響應傳遞給客戶端。下一個 HTTP 請求提供實體的修改內容,這些內容必須重新創建、附加到新上下文並持久化。娛樂通常發生在上下文範圍之外(具有持久性無知的分層架構)。

解決方案

那麼如何處理這種斷開連接的場景呢?使用 POCO 類時,我們有 3 種方法來處理更改跟踪:

  • 快照 - 需要相同的上下文 = 對於斷開連接的場景無用
  • 動態跟踪代理 - 需要相同的上下文 = 對斷開連接的場景無用
  • 手動同步。

在單個實體上手動同步很容易。您只需要附加實體並呼叫 AddObject 進行插入,DeleteObject 刪除或將 ObjectStateManager 中的狀態設置為 Modified 進行更新。當您必須處理對像圖而不是單個實體時,真正的痛苦就來了。當您必須處理獨立關聯(不使用外鍵屬性的關聯)和多對多關係時,這種痛苦會更加嚴重。在這種情況下,您必須手動同步對像圖中的每個實體以及對像圖中的每個關係。

MSDN文件提出了手動同步作為解決方案:附加和分離對像說:

對像以 Unchanged 狀態附加到對像上下文。如果您因為知道對像在分離狀態下被修改而需要更改對象的狀態或關係,請使用以下方法之一。

提到的方法是 ObjectStateManager 的 ChangeObjectState 和 ChangeRelationshipState = 手動更改跟踪。類似的提議在其他 MSDN 文件文章中:定義和管理關係說:

如果您正在使用斷開連接的對象,則必須手動管理同步。

此外,還有一篇與 EF v1 相關的部落格文章準確地批評了 EF 的這種行為。

解決原因

EF 有許多“有用的”操作和設置,如RefreshLoadApplyCurrentValuesApplyOriginalValuesMergeOption等。但根據我的調查,所有這些功能僅適用於單個實體,並且僅影響標量屬性(= 不影響導航屬性和關係)。我寧願不使用嵌套在實體中的複雜類型來測試這種方法。

其他建議的解決方案

EF 團隊沒有提供真正的合併功能,而是提供了一種稱為自我跟踪實體(STE) 的東西,它不能解決問題。首先,只有在整個處理過程中使用相同的實例時,STE 才有效。在 Web 應用程序中,除非您將實例儲存在視圖狀態或會話中,否則情況並非如此。因此,我對使用 EF 非常不滿意,我將檢查 NHibernate 的功能。第一次觀察說 NHibernate 可能有這樣的功能

結論

我將通過指向MSDN 論壇上另一個相關問題的單個連結來結束這個假設。檢查 Zeeshan Hirani 的答案。他是Entity Framework 4.0 Recipes的作者。如果他說不支持對像圖的自動合併,我相信他。

但是仍然有可能我完全錯了,並且 EF 中存在一些自動合併功能。

編輯2:

正如您所看到的,這已在 2007 年作為建議添加到MS Connect。MS已將其關閉,作為在下一個版本中要做的事情,但實際上除了 STE 沒有做任何事情來改善這個差距。

引用自:https://stackoverflow.com/questions/3635071