2014-04-01 20 views
4

ObjectStateManager上的大多數帖子都是基於唯一主鍵的真正重複問題。我的問題是我的表沒有主鍵,但它確實有多個外鍵,其中一個是Nullable。DBContext引發錯誤,附加唯一對象,可爲空鍵

class MyObject 
{ 
    int Key1; 
    int? Key2; 
} 


context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = null; }); 
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = 2000; }); **** 

即使這是數據庫中的唯一行,它會在第二次調用時爆發。

有關如何解決此問題的任何想法?或強制檢查兩個鍵?

+3

是否有任何理由要避免添加Id屬性作爲主鍵? –

+0

好點,我會在討論中提出這個問題。這會讓我的生活變得更容易。 – Steve

+0

它通常會! –

回答

0

作爲@BenAaronson提到,你應該在你的表中有一個代理主鍵在這個例子中。實體框架完全不能處理那些沒有定義主鍵的實體,事實上,我甚至感到你的代碼編譯/運行。也許你真正的代碼與真實的類和屬性名稱導致EF推斷使用其默認約定的主鍵。例如:

public class MyClass 
{ 
    public int MyClassId { get; set; } 
    public int MyOtherClassId { get; set; } 
} 

在上面的代碼中,即使沒有明確聲明它,EF將認爲MyClassId屬性是類MyClass主鍵,即使這可能不是你的意圖。

如果EF無法推斷主鍵並且沒有明確提供,那麼您的代碼將無法編譯(或者至多不會運行)。

因此,看看你的代碼,看起來發生的事情是,EF以某種方式推斷主鍵(在上例中,Key1)。然後,您嘗試一個新的對象附加到您的上下文:

context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = null; }); 

這導致在上下文中添加一個新的MyObject實例的主鍵值是100,其Key2屬性爲null

接下來,您嘗試另一個項目連接上下文:

context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = 2000; }); 

這樣做是嘗試新的項目添加到它的主鍵是100的背景下,這將失敗。這是因爲您的主鍵值爲100(由上面的第一條語句執行)已經有一個對象被上下文跟蹤。

由於您需要爲Key2屬性允許可能的null值,因此您不能使用複合主鍵,如前所述。所以,你需要關注@ BenAaronson的意見,並添加一個代理主鍵:

public class Object 
{ 
    // Alternatively, you can use a mapping class to define the primary key 
    // I just wanted to make the example clear that this is the 
    // surrogate primary key property. 
    [Key] 
    private int ObjectID { get; set; } // IIRC, you can make this private... 
    public int Key1 { get; set; } 
    public int Key2 { get; set; } 
} 

現在,你可以做到以下幾點:

context.MyTable.Add(new MyObject() { Key1 = 100, Key2 = null; }); 
context.MyTable.Add(new MyObject() { Key1 = 100, Key2 = 2000; }); 

通知我用Add方法,而不是Attach。這是因爲在使用Attach時,上下文假定您正在向數據庫中已存在的上下文添加對象,但未通過查詢將該對象帶入上下文;相反,您在內存中有一個表示形式,並且此時您希望上下文開始跟蹤對其進行的更改並在您撥打context.SaveChanges()時更新數據庫中的對象。在使用Attach屬性時,上下文將以Unmodified狀態添加對象。這不是我們想要的。我們將全新的對象添加到上下文中。所以我們使用Add。這告訴上下文添加該項目在Added狀態。您可以對其進行任何更改。由於這是一個新項目,因此它將處於Added狀態,直到您致電context.SaveChanges(),並且該項目被持久保存到您的數據存儲中,此時狀態將更新爲Unmodified

此時還需要注意一點。如果這是一個「多對多」的表,那麼你永遠不需要在EF中爲這種類型的連接表手動添加行(這裏有一些注意事項,見下文)。相反,您應該設置兩個對象之間的關係爲多對多的映射。也可以指定可選的多對多關係。如果第一個對象與第二個對象沒有關係,則第一個對象的連接表中不應該有行,反之亦然。

關於上面提到的連接表警告:如果你的連接表(即多對多映射表)很簡單(意味着表中唯一的列是那些將一個ID映射到相關ID的列),那麼您甚至不會將連接表視爲對象模型的一部分。此表由後臺的EF通過相關對象的導航屬性進行管理。但是,如果連接表包含的屬性不僅僅是相關對象的ID屬性(並且這意味着您有一個現有的數據庫或以這種方式顯式地構建了對象模型),那麼您將擁有一箇中間實體引用。例如:

public class A 
{ 
    public int ID { get; set; } 
} 

public class B 
{ 
    public int ID { get; set; } 
} 

public class AToB 
{ 
    // Composite primary key 
    [Key] 
    public int IdA { get; set; } 
    [Key] 
    public int IdB { get; set; } 

    public A SideA { get; set; } 
    public B SideB { get; set; } 

    // An additional property in the many-to-many join table 
    public DateTime Created { get; set; } 
} 

您也將有一定的映射告訴EF如何連接起來的外鍵關係。你會風是什麼了,然後在你的對象模型,如下:

myA.AToB.SideB // Accesses the related B item to this A item. 
myA.AToB.Created // Accesses the created property of AToB, telling you 
       // when the relationship between A and B was created. 

事實上,如果你有不平凡的連接表,如本例中,EF將始終包含它們在你的對象模型時從現有數據庫生成其模型。

我強烈建議您查看Julie Lerman和Rowan Miller關於編程實體框架的書籍。