0

I have been trying to update a user record from within the roles controller and keep hitting numerous errors. The latest error is the following exception:

System.InvalidOperationException: 'Attaching an entity of type 'TRIZTools.Models.User' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.'

Here's a copy of the code:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult RoleAddToUser(string UserId, string RoleName)
    {
        User user = context.Users.Where(u => u.Id.Equals(UserId, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
        var userManager = new UserManager<User>(new UserStore<User>(new ApplicationDbContext()));
        ViewBag.RolesForThisUser = userManager.GetRoles(user.Id);
        var idResult = userManager.AddToRole(user.Id, RoleName);
        if (user.AccountNew)
        {
            user.AccountNew = false;
            userManager.Update(user);
        }

It is failing at userManager.Update(user). I have tried the UpdateAsync, but that's failing too. Ideally what I want to do is set the user's new status to false, as this would make it easier to identify all the accounts that have been updated.

I am using Identity in my MVC framework and this method is not located in the AccountController.

Solution

With the assistance from @Rainman here is the the solution:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult RoleAddToUser(string UserId, string RoleName)
    {
        User user = context.Users.Where(u => u.Id.Equals(UserId, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
        var userManager = new UserManager<User>(new UserStore<User>(new ApplicationDbContext()));
        ViewBag.RolesForThisUser = userManager.GetRoles(user.Id);
        var idResult = userManager.AddToRole(user.Id, RoleName);
        if (user.AccountNew)
        {
            user.AccountNew = false;
            context.SaveChanges(); //This fixed it!
        }
TJS UK
  • 111
  • 2
  • 12
  • Possible duplicate of [ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value](https://stackoverflow.com/questions/23201907/asp-net-mvc-attaching-an-entity-of-type-modelname-failed-because-another-ent) – Chandan Rauniyar Nov 28 '17 at 21:51
  • 2
    Try using the UserManager to retrieve the User (UserManager.FindById()) instead of "context.Users.Where()". I think the issue is because you're using one context to retrieve the user and an entirely different context to update the user. – Matt M Nov 28 '17 at 22:31

2 Answers2

1

I didn't see the userManager.AddToRole method but I suspected that you probably updating another user entity inside the method. So the user entity is marked as EntityState.Modified and you are trying to update another user entity before it wasn't updated yet. So, we should save the first entity and EF will mark the entity as EntityState.Unchanged, in this way probably the problem will be solved. To add context.SaveChanges(); I modified the method like this;

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult RoleAddToUser(string UserId, string RoleName)
    {
        User user = context.Users.Where(u => u.Id.Equals(UserId, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
        var userManager = new UserManager<User>(new UserStore<User>(new ApplicationDbContext()));
        ViewBag.RolesForThisUser = userManager.GetRoles(user.Id);
        var idResult = userManager.AddToRole(user.Id, RoleName);
        context.SaveChanges(); // Add this line
        if (user.AccountNew)
        {
            user.AccountNew = false;
            userManager.Update(user);
        }
     }
lucky
  • 12,734
  • 4
  • 24
  • 46
  • Great work @Rainman, that almost worked. However, because of your response, I have now fixed it, so although you did not provide the solution, you provided me with the help I needed. – TJS UK Nov 30 '17 at 19:58
  • I put the solution within the IF statement, rather than in the main code block. – TJS UK Dec 04 '17 at 09:55
  • Ok, I saw it. My mistake. – lucky Dec 04 '17 at 10:35
1

Solution

With the assistance from @Rainman here is the the solution:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult RoleAddToUser(string UserId, string RoleName)
    {
        User user = context.Users.Where(u => u.Id.Equals(UserId, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
        var userManager = new UserManager<User>(new UserStore<User>(new ApplicationDbContext()));
        ViewBag.RolesForThisUser = userManager.GetRoles(user.Id);
        var idResult = userManager.AddToRole(user.Id, RoleName);
        if (user.AccountNew)
        {
            user.AccountNew = false;
            context.SaveChanges(); //This fixed it!
        }
TJS UK
  • 111
  • 2
  • 12