2009-09-02 40 views
42

我試圖從實體框架內更新複合主鍵的一個值,並且出現此錯誤:「屬性'CustomerID'是對象的關鍵信息的一部分, 。不能修改「使用實體框架更新主鍵值

這裏是我的代碼:

Dim customer As Customer = (From c In db.Customer Where c.CustomerID = "xxx" AndAlso c.SiteKey = siteKey).FirstOrDefault 
customer.CustomerID = "fasdfasdf" 
db.SaveChanges() 

這似乎太簡單了。這是真的,你不能更新實體框架內的主鍵?我找不到有關該主題的任何文檔。謝謝!

+8

請問爲什麼要改變主鍵,這是非常非常非常糟糕的做法。 – 2009-09-02 13:46:21

+1

好問題。我們通過匿名的customerid在數據庫中跟蹤匿名信息。當用戶登錄時,我想更新表格並將其設置爲其常規customerid。 – 2009-09-02 13:49:49

+4

只是我,我會有兩個表爲匿名用戶之一,然後他們登錄時在主表中創建一個新的記錄,但永遠不會修改主要。 – 2009-09-02 13:51:39

回答

10

你不能,並有很好的理由。請參閱KM評論。

一兩件事,我說你可以做的是有兩個表一個匿名的數據和一個他們登錄後存儲的真實用戶數據。

或者你可能(未測試或曾經通過我做的)被有這種表格佈局:

---Customers---- 
AutoNumber PK <- This links to all other tables in your database, and does NOT change. 
CustomerID <- This can change. 
CustomerType <- Anonymous or logged in. 

而當他們登錄時,您將CustomerType和CustomerID更改爲您所需的。

所以您的查詢看起來是這樣的:

Dim customer As Customer = (From c In db.Customer _ 
          Where c.CustomerID = {Some temp ID} _ 
          AndAlso c. CustomerType = "Anonymous").FirstOrDefault 
// After user logs in. 
customer.CustomerID = {Make a new user ID here} 
customer.CustomerType = "LoggedIn" {or what ever} 
db.SaveChanges() 

注意,自動編號主鍵從未變化。這樣,您與Customers表關聯的任何表仍然可以工作,並且不必在主鍵上進行級聯更新(就好像用鉛筆刺入眼睛一樣)。

+1

實體框架如何認爲某個關鍵是一個關鍵,並且我沒有看到它是數據庫中的關鍵字。那你怎麼辦呢?? – Dexter 2011-01-06 20:25:55

+35

我不同意這一點:「有充分的理由。」我知道一些時候這是糟糕的做法,但也有例外。例如,如果我使用自然/多部分密鑰存儲電話號碼,爲什麼我需要創建代理鍵才能更新電話類型?英孚應該放棄我的業務,​​並按照我的要求去做。這是系統的一個壞的侷限。 – 2011-03-16 22:30:06

+2

「一些時間不好的做法」這在所有的時候都是不好的做法。我從來沒有見過能夠改變主鍵的理由。如果你只有一個表,那麼沒有什麼大不了的,但是如果你有鏈接表並且你開始搞亂主鍵的話,它會變得非常混亂。 – 2011-03-17 02:26:48

19

您不能通過實體框架更新主鍵,因爲實體框架不知道要更新哪個數據庫行。但是,如果您確實需要這樣做,則可以編寫一個存儲過程來更新主鍵,然後從實體框架中執行存儲過程。

+1

-1你應該永遠不需要這樣做。你的數據庫設計是錯誤的。 – 2009-09-03 01:30:47

+8

@Nathan,我同意你的看法,它不應該是必然的,我們只使用自動增量ID。然而,問題是可能的,這是一種方法。 – 2009-09-03 06:30:32

+0

設拉子:這是要走的路。 @Nathan:我不明白爲什麼更新自然鍵是錯誤的數據庫設計。見Dr. A'a答案。 – 2012-01-18 14:07:20

-3

您不能更新主鍵,但這不是實體框架的限制,而是數據庫開發的一個非常基本的規則。主鍵被分配一次到一個表中的一行,並使這一行是唯一的。
也許你可以在某些方面更新密鑰,但這絕對違反了主鍵的定義。

所以,只是不要這樣做,還有其他方法可以完成您正在嘗試做的事情。

+1

這是一個過時的經驗法則,當然不是有意義的。因爲col上的級聯更新一切都保持同步,除非你有數據在數據庫之外(這很糟糕),這沒有問題 – markmnl 2012-08-10 01:58:50

53

我有一個博士學位。在cs中 - 在數據庫領域,所以這個答案會有點不同於程序員的觀點。對於Oliver Hanappi來說,如果它不是代理鍵,那麼一個關鍵可以偶爾也會改變。例如。一個自然鍵或一個合成鍵。例如。有可能在美國更改SSN。但是很多程序員多年來一直認爲這是一個不可改變的關鍵,並將其用於此。更改由外鍵組成的複合主鍵更爲常見。

我在處理一個具有三元關係的數據庫。具體來說有三個實體(外鍵在它們各自表中的代理主鍵)。但是,要保留兩個實體之間的關係,同時更改第三個實體需要更改部分交集表(也稱爲MSDN上的純聯接表)主鍵。這是一個有效的設計,只能通過刪除三元關係交點表並用兩個二元關係表(可能有它們自己的代理鍵)替換它來改進。 EF會處理這個罰款。這種設計變更會使(Many-> many) - > many或Parent1-Parent2 - > Child-grandchild模型(如果不清楚,請閱讀下面的示例)。實體框架可以正常工作,因爲每個關係實際上都是一對多的關係。但從數據庫的角度來看這是一個瘋狂的設計。讓我告訴你一個例子爲什麼。

考慮課程,課堂和講師在課堂上彼此關聯。 Class可以包括:CourseID,ClassroomID,InstructorID作爲外鍵,幷包含一個包含所有三個鍵的組合主鍵。雖然一個清晰,簡潔的三元模型(3路關係),我們可以將其分解爲二元關係。這會得到兩個相交表。

類(SurrogateKeyClassInstructorIDCourseID

ClassRoomUsed(SurrogateKeyClassroomUsedSurrogateKeyClassClassRoomID

的:如下添加代理鍵將滿足EF這個設計的問題是我們可以有相同的課程和講師相關的mult這是以前的模型避免的時間。爲了避免這個問題,你可以在數據庫中添加一個約束來保證兩個ID字段的唯一性,但是爲什麼當你只處理代理鍵開始的時候,你會這麼做?然而,這個解決方案將盡我所能地工作。然而,這不是邏輯數據庫設計,因爲DB中需要不自然的唯一約束。

但是,如果你不想改變你的數據庫或不能改變你的數據庫,這裏是一個第二個解決方案:路口/關聯表是正義的,鏈接連接兩個實體或更多在一起。如果更改,請刪除關聯並重新創建一個具有適當外鍵(導航屬性)的新關聯。這意味着你不會被允許在任何關係中要求兒童實體,但這是非常普遍的。

我會建議實體框架(在未來)允許我們這些誰可以設計一個優雅的數據庫模型來改變我們想要的交集/關聯表中的部分鍵!

另一個例子免費:

考慮一個學生,當然,等級協會。學生通過一個等級與課程相關聯。通常這是學生和課程之間的多對多關聯,並在關聯表中添加了一個稱爲等級的附加字段(關聯表具有有效載荷數據,例如等級,交叉表不具有有效載荷,並且在MSDN中被稱爲純連接表租賃在一個地方):

學生(StudentID,....)

場(CourseID,...)

服用(StudentIDCourseID,級)

如果有人從下拉使得數據錄入錯誤,並把學生在錯誤的類,你喜歡他們通過選擇日後變更再次下拉並選擇不同的課程。在後臺,您需要從Taking表中刪除EF對象,並重新創建它,而不會丟失成績(如果有的話)。只需更改外鍵CourseID似乎是一個更好的選擇。如果這個人看起來有些做作,可以拿出你自己的協會,但作爲教授,這對我來說很自然。

結論:當你有一串關係時,最好不要允許級聯和/或改變FK,但是在需要時存在合理/合理的場景,即使不推薦作爲的最佳實踐一般

這個問題可能會表現有以下例外取決於如果要更改導航屬性或模型分別關鍵屬性:

參照完整性約束衝突發生了:主鍵屬性,它是一個組成部分如果依賴對象未更改,除非將其設置爲關聯的主體對象,否則無法更改參照完整性約束。主要對象必須被追蹤並且不被標記爲刪除。

屬性'X'是對象的關鍵信息的一部分,無法修改。

+0

我正是這種情況,你的意見是現貨。 – xan 2012-12-31 14:06:58

+1

非常好的答案。我通常使用代理PK「爲了一致性」(並根據需要引入其他CK),除非用作連接表,它們總是看起來像一個可怕的雜湊物。這個例子很有意義。 – user2246674 2013-07-17 22:36:23