2012-04-19 69 views
7

我在Navigations表的列中有一個唯一約束,名爲Index。我有兩個Navigation實體,我想交換他們的Index值。在實體框架中使用唯一約束交換值

當我呼叫db.SaveChanges時,它會拋出一個異常,指出違反了唯一約束。看來EF正在更新一個值,然後是另一個值,從而違反了約束條件。

它不應該在事務中更新它們,然後嘗試提交一次值,而不是違反約束?

有沒有辦法解決這個問題,而不使用臨時值?

+1

你能展示一些代碼嗎? – Likurg 2012-04-19 10:58:53

+0

這裏你需要臨時值,更新是一個自包含的操作。所以你總是會違反約束條件,唯一的辦法是禁用操作的約束條件。 – 2012-04-19 11:07:03

回答

9

這不是EF的問題,而是SQL數據庫的問題,因爲更新命令是按順序執行的。事務與此無關 - 所有約束都按每個命令而不是每個事務進行驗證。如果你想交換唯一的值,你需要更多的步驟,你將使用額外的虛擬值來避免這種情況。

+0

這看起來很危險,因爲潛在的問題可能會出現使用相同虛擬值的併發請求,或者也可能是違反約束的虛擬值。您知道這種情況有沒有「最佳做法」? – 2012-04-19 11:07:40

+1

您也可以刪除舊記錄並創建新記錄。我會盡力避免這一點。這是唯一約束不適合您的應用程序邏輯需求的情況。 – 2012-04-19 11:12:23

+0

夠公平的。我只是要從數據庫中刪除唯一的約束。如果有重複,它不會造成任何問題。如果有的話,當他們顯示在導航欄上時,他們會處於不可靠的順序。 – 2012-04-19 11:26:52

2

您可以運行一個自定義的SQL查詢來交換值,如:

update Navigation 
set valuecolumn = 
     case 
      when id=1 then 'value2' 
      when id=2 then 'value1' 
     end 
where id in (1,2) 

然而,實體框架不能這樣做,因爲它是一個ORM的範圍之內。它只是針對每個被更改的實體執行連續的update陳述,就像他在答案中描述的拉迪斯拉夫一樣。

另一種可能性是放棄數據庫中的UNIQUE約束,並依賴應用程序正確執行此約束。在這種情況下,EF可以保存更改,但取決於您的情況,這可能是不可能的。

3

有幾種方法。其中一些內容在其他答案和評論中有所涉及,但爲了完整起見,我將在這裏列出它們(請注意,這僅僅是我頭腦風暴的列表,可能並不完全)。

  1. 在單個命令中執行所有更新。有關此示例,請參閱W0lf's answer
  2. 執行兩組更新 - 一個將所有值交換爲預期值的負值,然後一秒將其從負值交換爲正值。這正在假設負值不被其他限制所阻止,並且它們不是那些記錄在瞬態下的記錄以外的值。
  3. 添加一個額外的列 - 例如IsUpdating - 在更改值的第一組更新中將其設置爲true,並在第二組更新中將其更改爲false。交換過濾的唯一索引的唯一約束,該索引忽略IsUpdating爲true的記錄。
  4. 刪除約束並處理重複值。
+0

設置爲負值然後回到正值的想法很聰明,併爲我做了訣竅。謝謝! – jslatts 2016-07-14 02:21:08