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 User
s , 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");
}
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");
}