userManager.AddToRoleAsync() - Error: role does not exist

jmk22 picture jmk22 · Sep 15, 2016 · Viewed 21k times · Source

I'm creating a user registration system using .NET Core, Identity Core, and MVC Core. I'm able to create users and create roles in the database.

Here's the form on the view that lets me select a user and select a role to add:

@using (Html.BeginForm("AddRoleToUser", "Roles"))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <p>
        Username : @Html.DropDownList("UserName", (IEnumerable<SelectListItem>)ViewBag.Users, "Select ...")
        Role Name: @Html.DropDownList("RoleName", (IEnumerable<SelectListItem>)ViewBag.Roles, "Select ...")

    </p>

    <input type="submit" value="Save" />
}

These drop-down lists are populated with users and roles that already exist in the database. They allow me to select Users , and the name of a role that I've already created. For example, I have a role with the name "admin", this form lets me select the string "admin".

Here's the action that handles adding a role to a user:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> AddRoleToUser(string UserName, string RoleName)
    {
        try
        {
            ApplicationUser user = _db.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
            await _userManager.AddToRoleAsync(user, RoleName);
            PrepopulateDropDownMenus();
            ViewBag.ResultMessage = "Role created successfully!";
            return View("Manage", "Roles");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            return View("Manage");
        }
    }

The action never adds the role to the user, and the exception reads "Role "ADMIN" does not exist." with no inner exception. I've tried turning the RoleName in the action parameters to all-caps, but it still does not find the role. I've also tried using the role ID instead of the name, which was also unsuccessful.

This exact code worked when I built this app using Identity 3.0 with MVC 6. It seems like something has changed in moving over to Identity Core.

Any thoughts?

Edit

Here's the code I'm using to populate the drop-down lists in RolesController via the Viewbag:

    private void PrepopulateDropDownMenus()
    {
        var rolesList = _db.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
        var usersList = _db.Users.OrderBy(u => u.UserName).ToList().Select(uu => new SelectListItem { Value = uu.UserName.ToString(), Text = uu.UserName }).ToList();
        ViewBag.Roles = rolesList;
        ViewBag.Users = usersList;
    }

Here's how I add Identity in Startup.cs in the ConfigureServices method:

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddEntityFramework()
            .AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
    }

Here's the route in RolesController.cs I use to create a new role in the database:

    [HttpPost]
    public IActionResult Create(string rolename)
    {
            _db.Roles.Add(new IdentityRole()
            {
                Name = rolename
            });
            _db.SaveChanges();
            ViewBag.ResultMessage = "Role created successfully!";
            return RedirectToAction("Index");
    }

Answer

FranksBrain picture FranksBrain · Sep 16, 2016

I can't post comments yet to ask you, so, does your error say user admin does not exist, or role does not exist? I tried to duplicate your code on my end, and if the user doesn't exist you'll get a "user can't be null" error. However, if the role doesn't exist, you'll get a "Role [role] does not exist" error.

I assume that you already have the role added into your database? Here is some code I used in my seed method that essentially does what you want, minus using the views to do it:

        // Add the Admin role to the database
        IdentityResult roleResult;
        bool adminRoleExists = await _roleManager.RoleExistsAsync("Admin");
        if (!adminRoleExists)
        {
            _logger.LogInformation("Adding Admin role");
            roleResult = await _roleManager.CreateAsync(new IdentityRole("Admin"));
        }

        // Select the user, and then add the admin role to the user
        ApplicationUser user = await _userManager.FindByNameAsync("sysadmin");
        if (!await _userManager.IsInRoleAsync(user, "Admin"))
        {
            _logger.LogInformation("Adding sysadmin to Admin role");
            var userResult = await _userManager.AddToRoleAsync(user, "Admin");
        }

EDIT

The way you're adding roles right now leaves the NormalizedName field in the Role table null, which I believe is used by the framework for adding roles to users. Try one of the following to add a role to the database instead of what you're currently doing:

var result = await _roleManager.CreateAsync(new IdentityRole(rolename));

Or this may also work (haven't tested this one though):

[HttpPost]
public IActionResult Create(string rolename)
{
        _db.Roles.Add(new IdentityRole()
        {
            Name = rolename,
            NormalizedName = rolename.ToUpper()
        });
        _db.SaveChanges();
        ViewBag.ResultMessage = "Role created successfully!";
        return RedirectToAction("Index");
}