这似乎应该是非常明显的,但实体框架的某些内容让我感到困惑,我无法让它工作。
很简单,我有三个表,Id值是标识列: 用户(userId,用户名) 类别(categoryId,categoryName) JoinTable(UserId,CategoryId)复合。
在实体设计器(这是.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 :(得分:5)
在将User和Category实体添加到数据库之后,您需要调用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。为什么EF在复合PK存在的生成阶段这样做是超出我的。我将向MS发布有关此问题的错误,但同时您可以在生成后手动编辑edmx / ssdl文件以删除Identity说明符。在JoinTable的EntityType标记的Property标记下找到StoreGeneratedPattern =“Identity”字符串并将其删除:
变化:
<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 :(得分:1)
我这样做的方法是先用实体键生成一个有效的Category实体。
Category myCategory = _context.Categories.First(i => i.CategoryID == categoryIDToUse);
或者您可以尝试将实体创建为存根以将命中保存到数据库:
Category myCategory = new Category{CategoryID = categoryIDToUse };
然后使用AttachTo方法将该实体添加到ObjectContext上的实体集(CategorySet)(您可能想要检查它是否已经附加)。然后,您可以使用Add方法将Category添加到User实体。像这样:
myUser.Categories.Add(myCategory);
调用SaveChanges()。这对我有用。
答案 2 :(得分:0)
当新的父级添加到Context时,对象树中所有对象的状态将更改为“已添加”,因此EF会尝试将所有此类对象保存为新记录。
参考此链接: http://nileshhirapra.blogspot.in/2012/03/entity-framework-insert-operation-with.html