Updating user role using asp.net identity

2019-03-21 13:28发布

I have the following problem. While using the following code below to change the user's current role i am getting an exception with the message like below:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public virtual ActionResult Edit(User user, string role)
    {
        if (ModelState.IsValid)
        {
            var oldUser = DB.Users.SingleOrDefault(u => u.Id == user.Id);
            var oldRoleId = oldUser.Roles.SingleOrDefault().RoleId;
            var oldRoleName = DB.Roles.SingleOrDefault(r => r.Id == oldRoleId).Name;
            if (oldRoleName != role)
            {
                Manager.RemoveFromRole(user.Id, oldRoleName);
                Manager.AddToRole(user.Id, role);
            }
            DB.Entry(user).State = EntityState.Modified;

            return RedirectToAction(MVC.User.Index());
        }
        return View(user);
    }

Attaching an entity of type 'Models.Entities.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.

Does anybody know a good solution to this problem ?

2条回答
对你真心纯属浪费
2楼-- · 2019-03-21 13:53

The problem is that your Manager and DB doesn't use the same DbContext. So when you send an user from the context of your DB to the Manager it will handle it as a "new" one - and then you cant remove it from the role. You have two ways to go here. The easiest is to get the User from your Manager.

[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult Edit(User user, string role)
{
    if (ModelState.IsValid)
    {
        // THIS LINE IS IMPORTANT
        var oldUser = Manager.FindById(user.Id);
        var oldRoleId = oldUser.Roles.SingleOrDefault().RoleId;
        var oldRoleName = DB.Roles.SingleOrDefault(r => r.Id == oldRoleId).Name;

        if (oldRoleName != role)
        {
            Manager.RemoveFromRole(user.Id, oldRoleName);
            Manager.AddToRole(user.Id, role);
        }
        DB.Entry(user).State = EntityState.Modified;

        return RedirectToAction(MVC.User.Index());
    }
    return View(user);
}

The more elegant way is to start using an DI-framework like AutoFac (https://code.google.com/p/autofac/wiki/MvcIntegration) and set your DbContext as InstancePerApiRequest.

builder.RegisterType<YourDbContext>().As<DbContext>().InstancePerApiRequest();
查看更多
疯言疯语
3楼-- · 2019-03-21 14:15

My roles are managed in the seed method of my DbMigrationsConfiguration class and I renamed it like this:

        if (context.Roles.Any(r => r.Name == "User"))
        {
            var store = new RoleStore<IdentityRole>(context);
            var manager = new RoleManager<IdentityRole>(store);
            var role = manager.Roles.First(r => r.Name == "User");

            role.Name = "NewNameForUser";
            manager.Update(role);
        }
查看更多
登录 后发表回答