2010-02-11 65 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