Jammer
Jammer

Reputation: 10208

ASP.NET Identity UserManager UpdateAsync Removing Roles

There is a screen in our application that allows members of the Administrators role to edit user account details.

Ultimately any user object is sent to the server and updated using:

await userManager.UpdateAsync(user);

This is working as expected for updating user records. We can make changes that are persisted to the database such as usernames, phone numbers etc.

The issue I'm seeing is that when updating the Roles sometimes rather than adding an additional role to the user it removes all the roles.

On our ApplicationUser object we have a property like this:

public virtual ICollection<IdentityUserRole<string>> Roles { get; set; } = new List<IdentityUserRole<string>>();

So we can bounce roles between client and server as part of a user object.

What I'm struggling to understand is the inconsistent behavior of the userManager.UpdateAsync(user); method I'm seeing. It clearly works or it would never be capable of adding any role to a user.

The object arriving at the server is correctly populated with the roles each time it is submitted. The basic flow is:

  1. Add role and submit
  2. The role is added to the user correctly
  3. Add additional role and submit
  4. All Roles are removed

How can I prevent it from removing roles?

Thanks!

UPDATE

As detailed in the answer from @amirani I've tried filtering the existing list instead. I've setup AutoMapper to ignore the property entirely.

.ForMember(x => x.Roles, opt => opt.Ignore());

And am now just filtering like this (only adding at the moment):

foreach (var userDtoRole in userDto.Roles)
{
  if (user.Roles.FirstOrDefault(x => x.RoleId == userDtoRole) == null)
  {
    user.Roles.Add(new IdentityUserRole<string> {RoleId = userDtoRole, UserId = user.Id });
  }
}

The additional roles are never added to the underlying database. I've confirmed that the user object being updated has the correct list of roles prior to executing the UpdateUser method.

Upvotes: 11

Views: 2333

Answers (2)

Edward
Edward

Reputation: 29976

Try code below to add new roles

private readonly ApplicationDbContext _context;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<IdentityRole> _roleManager;
    public HomeController(ApplicationDbContext context
        , UserManager<ApplicationUser> userManager
        , RoleManager<IdentityRole> roleManager)
    {
        _context = context;
        _userManager = userManager;
        _roleManager = roleManager;
    }
    public async Task<IActionResult> CreateUserAndRole()
    {
        await _userManager.CreateAsync(new ApplicationUser { UserName = "[email protected]", Email = "[email protected]" });
        await _roleManager.CreateAsync(new IdentityRole {  Name = "Admin" });
        await _roleManager.CreateAsync(new IdentityRole { Name = "Administrator" });
        return Ok();
    }
    public async Task<IActionResult> AddAdminRoleToUser()
    {
        ApplicationUser user = _context.Users.FirstOrDefault(u => u.UserName == "[email protected]");
        var role = await _roleManager.FindByNameAsync("Admin");
        user.Roles.Add(new IdentityUserRole<string> { RoleId = role.Id, UserId = user.Id });
        await _userManager.UpdateAsync(user);
        return Ok();
    }
    public async Task<IActionResult> AddAdministratorRoleToUser()
    {
        ApplicationUser user = _context.Users.FirstOrDefault(u => u.UserName == "[email protected]");
        var role = await _roleManager.FindByNameAsync("Administrator");
        user.Roles.Add(new IdentityUserRole<string> { RoleId = role.Id, UserId = user.Id });
        await _userManager.UpdateAsync(user);
        return Ok();
    }

Upvotes: 1

amirani
amirani

Reputation: 270

When you call await userManager.UpdateAsync(user);You are updating user model with all its related data. You need to add new role to an existing roles list instead of creating new empty list and adding new record.

Upvotes: 2

Related Questions