2010-02-11 63 views
5

這看起來應該是很明顯的,但是關於實體框架的一些事情讓我感到困惑,我無法實現這個目標。如何在.net實體框架中插入或更新多對多的表格

很簡單,我有三個表,其中ID值標識列: 用戶(用戶ID,用戶名) 分類(的categoryId,類別名) JoinTable(用戶ID,類別ID)的複合材料。

在實體設計器(這是.net 4.0)中,當我導入這些表時,如預期那樣,連接表不出現,但用戶和類別顯示關係。下面的代碼:

var _context = new MyContext(); 
var myUser = new User(); 
myUser.UserName = "joe"; 

var myCategory = new Category(); 
myCategory.CategoryName = "friends"; 

_context.Users.AddObject(myUser); 
myUser.Categories.Add(myCategory); 

var saved = _context.SaveChanges(); 

返回一個錯誤(雖然沒有被添加到數據庫中):

An item with the same key has already been added. 

如果我添加保存之前如下:

_context.Categories.AddObject(myCategory); 
myCategory.Users.Add(myUser); 

我得到的同樣的錯誤,沒有保存到數據庫。如果我保存MYUSER和myCategory對象嘗試將它們聯繫起來之前,他們兩個都節省,但第二保存引發錯誤,什麼也沒有添加到連接表:

Cannot insert the value NULL into column 'UserId', table '...dbo.JoinTable'; column does not allow nulls. INSERT fails. The statement has been terminated. 

,我明顯沒有理解多少插入許多關係。我錯過了什麼?

+0

什麼是你的ID屬性的類型? – 2010-02-11 13:51:20

+0

int,標識 – 2010-02-11 17:06:54

+0

仍試圖找出。通過向連接表(DateCreated)添加一個虛假的第三個字段,我可以強制實體設計器顯示連接表。然後我可以定義這些關係: var myJoin = new JoinTable {UserId = myUser.UserId,CategoryId = myCategory.CategoryId}; 然後,以下內容將在保存時正確插入: _context.Categories.AddObject(myCategory); myCategory.JoinTables.Add(myJoin); myJoin.User = myUser; – 2010-02-11 19:39:52

回答

5

將用戶和類別實體添加到數據庫後,您確實需要調用SaveChanges(),然後設置它們之間的關聯。

但是,這裏真正的問題是您列出的第二個異常。如果你看一下SqlProfiler或在調試器中的ADO.NET探查,你會看到第二個的SaveChanges時調用它看起來是這樣的:

insert [dbo].[JoinTable]([UserId]) values (@0) select [CategoryId] from 
[dbo].[JoinTable] where @@ROWCOUNT > 0 and [UserId] = @0 and [CategoryId] = scope_identity() 

顯然,如果你正確設定你的JoinTable這是不行的(兩欄中的複合PK)。

如果我通過模型瀏覽器查看EntityModel存儲,它顯示JoinTable中的CategoryId列確實已將StoreGeneratedPattern設置爲Identity,而UserId設置爲None。爲什麼在這個世代階段,當一個複合PK出現時,EF做到了這一點超出了我的想象。我將向MS發佈一個關於此的錯誤,但同時您可以在生成後手動編輯edmx/ssdl文件以刪除Identity說明符。找到StoreGeneratedPattern = 「身份」 的字符串爲您JoinTable的的EntityType標籤的屬性標籤下將其刪除:

變化:

<Property Name="CategoryId" Type="int" Nullable="false" StoreGeneratedPattern="Identity" /> 

要:

<Property Name="CategoryId" Type="int" Nullable="false" /> 

然後,當你運行你代碼,你會得到一個更好的插入查詢(並沒有更多的例外!):

insert [dbo].[JoinTable]([UserId], [CategoryId]) values (@0, @1) 
1

我這樣做的方式是先用實體鍵生成一個有效的Category實體。

Category myCategory = _context.Categories.First(i => i.CategoryID == categoryIDToUse); 

或者你可以嘗試創建實體作爲存根保存命中到DB:使用AttachTo上的ObjectContext

Category myCategory = new Category{CategoryID = categoryIDToUse }; 

然後該實體添加到實體集(CategorySet)方法(你可能想檢查它是否已經連接)。然後,您可以使用Add方法將類別添加到您的用戶實體。類似這樣的:

myUser.Categories.Add(myCategory); 

Call SaveChanges()。這對我有效。

+0

也許這是4.0的變化,但myUser下沒有'categoryset'對象,但有'categories': myUser.Categories.Add(myCategory); 但是,我已經在我的代碼示例上面。我不確定你的意思是'用實體鍵生成一個有效的類別'的部分,但是? – 2010-02-11 18:23:11

+0

我正在使用EF v1.0。我更新了我的答案。通過'categoryset'我的意思是任何你的EntitySet名稱爲類實體。我看到它是'類別',我更新了代碼示例。我希望這有幫助。 – DaveB 2010-02-11 19:07:15

+0

對,不幸的是,這是我對上面顯示的結果所做的。使用:myCategory.Users.Add(myUser)AND/OR myUser.Categories.Add(myCategory)返回'具有相同密鑰的項目已被添加' – 2010-02-11 19:45:56