You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Entity Framework添加导航属性时出现主键重复异常的问题求助

Entity Framework添加导航属性时出现主键重复异常的问题求助

问题场景与代码

我定义了以下实体类:

public abstract class Entity
{
    public Guid Id { get; set; }
    public Entity()
    {
        Id = Guid.NewGuid();
    }
}

public class Account : Entity
{
    public Account() { }
    public Account(string name, Integration integration, User creator, List<User> usersAccessRights)
    {
        Name = name;
        IsActive = integration.HasSuccessfullyConnected;
        CreatedAt = DateTimeOffset.Now.ToUnixTimeSeconds();
        Integration = integration;
        Creator = creator;
        Users.AddRange(usersAccessRights);
    }
    public string Name { get; private set; }
    public long CreatedAt { get; protected set; }
    public bool IsActive { get; protected set; }
    public Guid CreatedBy { get; protected set; }
    public virtual User Creator { get; set; }
    public Guid IntegrationId { get; private set; }
    public virtual Integration Integration { get; private set; }
    public virtual List<User> Users { get; private set; } = new List<User>();
}

public class User : Entity
{
    public User() { }
    public User(string name, string email, string phone, string password)
    {
        Name = name;
        Email = email;
        Phone = phone;
        Password = password;
    }
    public string Name { get; private set; }
    public string Email { get; private set; }
    public string Phone { get; private set; }
    public string Password { get; private set; }
    public virtual List<Account> Accounts { get; set; } = new List<Account>();
}

public class Integration : Entity
{
    public Integration() { }
    protected Integration(string name, bool hasSuccessfullyConnected)
    {
        Name = name;
        IsActive = HasSuccessfullyConnected = hasSuccessfullyConnected;
        CreatedAt = DateTimeOffset.Now.ToUnixTimeSeconds();
    }
    public string Name { get; protected set; }
    public long CreatedAt { get; protected set; }
    public bool IsActive { get; protected set; }
    public bool HasSuccessfullyConnected { get; protected set; }
    public string Type { get; protected set; }
    public virtual string IntegrationType() => throw new NotImplementedException();
}

仓库方法:

private TEntity AddEntity(TEntity entity)
{
    _context.Set<TEntity>().Add(entity);
    _context.SaveChanges();
}

public virtual TEntity GetById(Guid id)
{
    return _context.Set<TEntity>().Find(id);
}

在创建Account时,我先查询到Creator User和有权限的User列表,然后构造Account并添加:

var account = CreateAccount(createAccountRequest.Name, integration, user, usersWithAccess);
var createAccountResult = _accountRepository.Create(account);

结果触发了以下异常:

SqlException: Violation of PRIMARY KEY constraint 'PK_User'. Cannot insert duplicate key in object 'dbo.User'. The duplicate key value is (3c8a0035-4bc0-4001-bfa9-498d182a7f36). The statement has been terminated.

明明只是关联已存在的User,却被EF当成新用户插入,尝试了多种方法都没解决,直到调整了操作顺序:

var createAccountResult = _accountRepository.Create(account);
_accountRepository.Commit();
account.Creator = creatorResult.Entity;
account.Users.AddRange(usersAccessRights);
_accountRepository.Commit();

这次就正常工作了。


问题原因分析

出现这个异常的核心原因是EF上下文对关联的User实体的追踪状态问题:
当你通过GetById查询到User后,如果这些User没有被当前上下文追踪(比如之前用了AsNoTracking(),或者上下文实例是新创建的),那么当你把这些User关联到新的Account上,再调用Add方法时,EF会遍历整个实体图,把所有未被追踪的关联实体都标记为Added状态——也就是说EF认为这些User是新的,要插入到数据库,自然就和已存在的主键冲突了。

你的解决方案为啥有效?

调整后的操作顺序,先把Account单独保存到数据库,此时Account已经被上下文追踪;之后再关联已查询的User,EF这时候只会识别出需要建立Account和User之间的关联关系(更新两者的中间关联表),而不会把User当成新实体插入,因此避免了主键重复的问题。

其他可行的解决方法

除了调整操作顺序,你还可以尝试这两种方案:

  1. 确保User被上下文追踪:检查你的查询逻辑,Find方法默认是会追踪实体的,但如果查询时使用了AsNoTracking(),会取消追踪。如果是这种情况,移除AsNoTracking()即可。
  2. 显式标记User为已存在状态:如果必须在构造Account时就关联User,可以在添加Account之前,手动告诉EF这些User是已存在的,不需要插入:
// 在调用AddEntity之前添加这段代码
_context.Entry(creator).State = EntityState.Unchanged;
foreach (var user in usersWithAccess)
{
    _context.Entry(user).State = EntityState.Unchanged;
}

这样EF就会将这些User标记为Unchanged状态,只会处理Account的新增和关联关系,不会尝试插入重复的User。

备注:内容来源于stack exchange,提问作者Vitor Archanjo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 07:50:31