违反了实体框架代码第一链接表中的PRIMARY KEY约束

本文关键字:链接表 PRIMARY 约束 KEY 实体 框架 代码 | 更新日期: 2023-09-27 18:20:57

我有一个User表和一个Roles表。有一个自动生成的UsersRoles链接表,其中包含User和Roles表中的Id。这是使用以下代码生成的:

modelBuilder.Entity<User>()
                .HasMany(u => u.Roles)
                .WithMany(r => r.Users)
                .Map(c => { 
                    c.MapLeftKey("UserId");
                    c.MapRightKey("RoleId");
                    c.ToTable("UsersRoles");
                });

当我试图添加一个不相关的实体并调用Context.SaveChanges()时,我收到以下错误:

违反PRIMARY KEY约束"PK_UsersRoles"。无法插入对象"dbo.UsersRoles"中存在重复键。重复键值为(2beaf837-9034-4376-9510-b1609c54efbe,dcd16d00-d46e-4d48-8328-3e7b35b11ccf)。声明已经终止。

我已经检查了Conext.ChangeTracker.Entries()中错误中提到的项目,实体状态标记为未更改。

唯一被标记为已添加的实体是我试图添加的新记录,其他所有内容都被标记为未更改。

添加实体的代码:

 RoleGroup group = Context.RoleGroups.Create();
 group.Title = roleGroupName;
 Context.Set<RoleGroup>().Add(group);
 Context.SaveChanges();

有人知道为什么会发生这种事吗?

违反了实体框架代码第一链接表中的PRIMARY KEY约束

为了补充Slauma的答案,如果您在这些自动多对多的东西中的一个上设置ICollection属性,EF会感到困惑,并且没有意识到您这样做是在清除collections元素。

因此,与其这样做:

user.Roles = new List<Role>();
user.Roles.Add(role);

你必须这样做:

user.Roles.Clear();
user.Roles.Add(role);

这对我有效。

不过你不应该这么做。这应该是EF中的一个错误。

带有UserId = 2beaf837-9034-4376-9510-b1609c54efbeUser和带有RoleId = dcd16d00-d46e-4d48-8328-3e7b35b11ccfRole处于Unchanged状态并不意味着不会向数据库写入任何内容。

特别是对于多对多关系(通常用于独立关联),EF保持关系本身的状态,该状态不同于实体状态。如果一个条目被插入到链接表中,则意味着所讨论的两个实体的关系条目处于状态Added,尽管这些实体的实体状态是Unchanged。枚举DbContextChangeTracker时,无法看到关系条目。它只返回实体状态。您必须转到底层ObjectContext来查询关系状态。

示例:

using (var ctx = new MyContext())
{
    var user = ctx.Users.Find(1);
    var role = ctx.Roles.Find(5);
    user.Roles = new List<Role>();
    user.Roles.Add(role);
    ctx.SaveChanges();
}

这里,userrole都将处于状态Unchanged,但仍有一条记录被插入到链接表中。如果数据库中已经链接了用户1和角色5,则此代码将引发异常。

添加group与问题无关。只有对SaveChanges的调用才会导致异常,因为您很可能在问题中的代码片段之前的某个地方创建了两个实体之间的关系。

您正试图将相同的用户/角色组合插入UserRoles表:

Violation of PRIMARY KEY constraint 'PK_UsersRoles'. Cannot insert duplicate key in object 'dbo.UsersRoles'. The duplicate key value is (2beaf837-9034-4376-9510-b1609c54efbe, dcd16d00-d46e-4d48-8328-3e7b35b11ccf). The statement has been terminated.

检查哪个用户的ID为2beaf837-9034-4376-9510-b1609c54efbe,哪个角色的ID为dcd16d00-d46e-4d48-8328-3e7b35b11ccf。如果你确信你只输入过该用户一次,那么插入数据的方法是否在你没有意识到的情况下被调用了不止一次?

希望有人会觉得这很有帮助,我发现在EF中更改现有列表与创建新列表有很大区别。

在这种情况下,我正在进行更新,并认为建立一个我想要的项目的新列表是最简单的。但是,如果一些项目已经添加到列表中,我在保存时会遇到主键冲突。

我最终保留了同一个列表,只是对它进行了修改。清除它并重新阅读是可以的,创建一个新列表则不然。