coolhand
coolhand

Reputation: 2061

.NET Core EF Primary Key Error on context.SaveChanges()

My Project model has a collection of <AppUsers>:

public class Project
    {
        public int ProjectID { get; set; }

        [Required(ErrorMessage = " Please enter a project name")]
        public string Name { get; set; }

        [Required(ErrorMessage = " Please enter a project description")]
        public string Description { get; set; }

        public virtual ICollection<AppUser> ProjectManagers { get; set; }
}

public class AppUser : IdentityUser
    {
       //just a placeholder for now until I add properties later
    }

In my EF repository, I get an error when I try to assign any user to more than one project as a part of the ProjectManager collection (e.g., I can assign them to Project1 but when I assign them to Project2 it throws an exception).

public void AddProjectManager(int projectID, AppUser user)
        {
            Project proj = context.Projects.Include(p => p.ProjectManagers).FirstOrDefault(p => p.ProjectID == projectID);
            if (!proj.ProjectManagers.Any(pm => pm.Id == user.Id))
            {
                AppUser appUser = identityContext.AspNetUsers.FirstOrDefault(p => p.Id == user.Id);
                if (appUser != null)
                {
                    proj.ProjectManagers.Add(appUser);
                    //ERROR OCCURS HERE WHEN I TRY TO SAVE
                    context.SaveChanges();
                }
            }
        }

I get the following error:

Violation of PRIMARY KEY constraint 'PK_AppUser'. Cannot insert duplicate key in object 'dbo.AppUser'. The duplicate key value is (xxx).

I've tried using a UserManager approach, but get the same error

Upvotes: 0

Views: 970

Answers (1)

Steve Py
Steve Py

Reputation: 34988

Normally this would result in a multiple instances of IEntityChangeTracker (see: Why is my entity being referenced by multiple instances of IEntityChangeTracker?) however I suspect your identityContext has lazy loading proxies turned off, so you'll get the PK exception.

The issue will be that you are loading the user from one context, (identityContext) and trying to save a reference to it via another context. (context) You want to load the reference from the same context. The application context knows about AppUser, but it didn't load the instance you are associating to the project because that was loaded from a different context so it is treating it as a new AppUser.

The solution is to load the AppUser from context rather than identityContext

public void AddProjectManager(int projectID, AppUser user)
{
    Project proj = context.Projects.Include(p => p.ProjectManagers).FirstOrDefault(p => p.ProjectID == projectID);
    if (!proj.ProjectManagers.Any(pm => pm.Id == user.Id))
    {
         AppUser appUser = context.AspNetUsers.Single(p => p.Id == user.Id);
         proj.ProjectManagers.Add(appUser);
         context.SaveChanges();
    }
}

If you don't have a DbSet for the AspNetUser in the application context, you would need to add it. Normally for something like this, I wouldn't add the entire AspNetUser entity to the application context, but rather a lightweight entity with just the details I need (ID and Name for instance) and then my application entities would reference this lightweight instance (pointed at the same table) to speed up data operations for this association.

Upvotes: 1

Related Questions