Identity: Why is user.roles empty? - c#

I have a method that gets all the users that i have in my db, simply put i do this:
var allUsers = context.Users.ToList();
What i can't figure it out is that when i debug the roles property it is empty:
but in dbo.UserRoles:
What am I missing here?
EDIT:
My registration method:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = model.FirstName, LastName = model.LastName };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
UserManager.AddToRole(user.Id, model.UserRole.ToString());
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return PartialView("~/Views/Account/Register.cshtml",model);
}
EDIT 2:
When getting the roles like this:
var roles = context.Roles.ToList();
I can see all the roles and I can also see which users have the specific role:
EDIT 3:
Tried turning lazyloading off and on
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.LazyLoadingEnabled = false;
Still doesn't give me the roles data.

You have to load related entities you want to use with Include like this :
var allUsers = context.Users.Include(u => u.Roles).ToList();
Then you should be able to access user roles.
More info about that topic here

So far I have not been able to solve this they way I want. I made a work arround that works:
I created a method that got me each individual user role like so:
public string GetRole(string userId)
{
var role = UserManager.GetRoles(userId);
return role[0];
}
and in my original Getuser method i called the my recently developed method:
public UserModel GetUsers()
{
var allUsers = context.Users.Include("Roles").ToList();
var model = new UserModel
{
Users = allUsers.Select(x => new OverWatchUser
{
Email = x.Email,
EmailConfirmed = x.EmailConfirmed,
FirstName = x.FirstName,
LastName = x.LastName,
OrgId = x.OrgId,
Role = GetRole(x.Id)
}).ToList()
};
return model;
}
This gives me the data I want but I consider this a dirty fix and I hope someone out there has a proper solution to the problem.

You can use context.Roles instead of context.Users as follows and filter the target user roles. The trick is to filter the target user roles in the where method.
string Id="the_required_user_Id";
var roles = context.Roles
.Include(r => r.Users)
.Where(r => (r.Users.Select(u => u.UserId).Contains(Id)))
.ToList();
This worked fine for me, hopefully this helps someone

Related

How to add a IdentityUser IdentityResult error in ASP.Net Core for a unique email address

My "User" class inherits from "IdentityUser" and I want to make the EmailAddress unique. BUT instead of creating a unique property like this
builder.Entity<User>()
.HasIndex(u => u.Email).IsUnique();
in the model builder that throws an exception when I try to register a duplicate email address in the method below, I'd like it to return a IdentityResult error, just like it does when I have a duplicate username. Some how Identity forgot to include a uniqueness for the email field!?
My register method where "result.Succeeded" is false if the username is taken/used and an IEnumerable of IdentityErrors, in "result.errors". I'd like to get the same type of error from a duplicate email. Is this possible?
[HttpPost("register")]
public async Task<IActionResult> Register(UserForRegisterDto userForRegisterDto)
{
var userToCreate = _mapper.Map<User>(userForRegisterDto);
var result = await _userManager.CreateAsync(userToCreate, userForRegisterDto.Password);
var userToReturn = _mapper.Map<UserForDetailedDto>(userToCreate);
if (result.Succeeded)
{
return CreatedAtRoute("GetUser", new { controller = "Users", id = userToCreate.Id }, userToReturn);
}
return BadRequest(result.Errors);
}
This is already supported, using RequireUniqueEmail, which defaults to false. Here’s an example of how to set it to true, taken from the docs and modified accordingly:
services.Configure<IdentityOptions>(options =>
{
options.User.RequireUniqueEmail = true;
});
You can achieve the same thing with the call to AddIdentity (or whichever variation you’re using). Here's an example of that approach:
services.AddIdentity<User, Role>(options =>
{
options.User.RequireUniqueEmail = true;
})

Identity usermanager cannot find the Role

I am trying to add a user to a role after successfully creating the user.
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (!ModelState.IsValid) return View(model);
var user = new ApplicationUser
{
UserName = model.PhoneNumber,
PhoneNumber = model.PhoneNumber,
NationalId = model.NationalId,
FullName = model.FullName
};
var result = await _userManager.CreateAsync(user, model.NationalId);
if (result.Succeeded)
{
var res = await _userManager.AddToRoleAsync(user, "Admin");
await _signInManager.SignInAsync(user, false);
_logger.LogInformation(3, "Applicant created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
// If we got this far, something failed, redisplay form
return View(model);
}
But, I get this error.
An unhandled exception occurred while processing the request.
InvalidOperationException: Role ADMIN does not exist.
Update:
I called the
var myrole = await _roleManager.FindByNameAsync("Admin");
and it returned null. but when i inspect
var roles = _roleManager.Roles
i get all the roles including "Admin"
I found the problem in the seed method. i do not understand it however.
in the seed method i used the RoleStore to add roles.
var roles = new[] {"Admin", "Applicant", "Student", "Role1", "Role2", "Role3", "Role4"};
foreach (var role in roles)
{
var roleStore = new RoleStore<IdentityRole>(context);
if (!context.Roles.Any(r => r.Name == role))
await roleStore.CreateAsync(new IdentityRole(role));
}
the roles where created successfully in the database table AspNetRoles.
but when acted upon, the roles were never found.
i replaced the RoleStore with RoleManager
await _roleManager.CreateAsync(new IdentityRole(role));
and like magic, it all worked out. i will do further research on the difference and the cause to understand it more.

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

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");
}
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");
}
Make sure when you are creating an AspNetRole the NormalizedName should not be null in order for the UserManager to work properly.
NormalizedName should be upper-case using .ToUpper()
Adding it straight to the database is a bad idea and violates every concept of encapsulation, and NormalizedName is not something you should be computing yourself.
See this answer
Replace this code:
var roleStore = new RoleStore<IdentityRole>(context);
await roleStore.CreateAsync(new IdentityRole(role));
with following:
var roleManager = services.GetService<RoleManager<IdentityRole>>();
await roleManager.CreateAsync(new IdentityRole(role));
This is the thing: if you are using major versions of identity (for example 6.0), the table AspNetRoles contains a Column named NormalizedName, if the string is null or differes from the role name written in capital letters you wont't be able to find the role name. You may insert manually (MMSMI) the string or delete per procedure all roles and recreate them.

How can I get the user role name in ASP.NET Update3?

I am currently doing an ASP.NET Web Application under MVC in C# using Individual User Accounts. I need to get the Role names for a user. In a previous version the following code would have worked:
foreach (var role in userObject.Roles)
{
appUser.RolesForUser.Add(role.Role.Name);
}
but now after doing Update 3, I now get:
Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole' does not contain a definition for 'Role' and no extension method 'Role' accepting a first argument of type 'Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole' could be found (are you missing a using directive or an assembly reference?
I would like to know how I can get role names with the new standard. Thanks for any solutions!
Try this:
//Get user roles
var userRoles = await UserManager.GetRolesAsync(user.Id);
Edited
If you have the userName instead of the userId, you have to retrieve the user using FindByNameAsync instead of FindByIdAsync.
var user = await UserManager.FindByNameAsync(myName);
Once you have the user object, you will be able to get the associated roles just doing:
var userRoles = await UserManager.GetRolesAsync(user.Id);
Example:
//
// GET: /Users/Edit/Bob
public async Task<ActionResult> Edit(string userName)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{
return HttpNotFound();
}
var userRoles = await UserManager.GetRolesAsync(user.Id);
return View(new EditUserViewModel()
{
Id = user.Id,
Email = user.Email,
RolesList = RoleManager.Roles.ToList().Select(x => new SelectListItem()
{
Selected = userRoles.Contains(x.Name),
Text = x.Name,
Value = x.Name
})
});
}

How to create ApplicationUser by UserManager in Seed method of ASP .NET MVC 5 Web application

I can create users in the old way:
var users = new List<ApplicationUser> {
new ApplicationUser{PasswordHash = hasher.HashPassword("TestPass44!"), Email = "informatyka4444#wp.pl", UserName = "informatyka4444#wp.pl", SecurityStamp = Guid.NewGuid().ToString()},
new ApplicationUser{PasswordHash = hasher.HashPassword("TestPass44!"), Email = "informatyka4445#wp.pl", UserName = "informatyka4445#wp.pl", SecurityStamp = Guid.NewGuid().ToString()}
};
users.ForEach(user => context.Users.AddOrUpdate(user));
context.SaveChanges();
but I want to do it the ASP.NET MVC 5.1 way using UserManager. I peeked how the Register POST method looks in AccountController:
public async Task<ActionResult> Register(RegisterViewModel model) {
if (ModelState.IsValid) {
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded) { [...]
so I tried do the same:
var user = new ApplicationUser() { Email = "informatyka4444#wp.pl",
UserName = "informatyka4444#wp.pl"};
IdentityResult result = UserManager.CreateAsync(user, "abcwq12312!P");
but I get this:
also If I just type UserManager. VS2013 does not shows any methods on the list.
So how to add user in this way?
EDIT1:
Ok so to create user CreateAsync is unnecessary the problem was somewhere else. One should use ApplicationUserManager not UserManager(this one did not add anything to the database).
var store = new UserStore<ApplicationUser>(context);
var manager = new ApplicationUserManager(store);
var user = new ApplicationUser() { Email = "informatyka4444#wp.pl", UserName = "informatyka4444#wp.pl" };
manager.Create(user, "TestPass44!");
I dont understand the error you are showing, unless you are providing a custom TUser or TKey in which case would be like :
IdentityResult user = await UserManager.CreateAsync<CustomUser, CustomKey>(user, "abcwq12312!P");
and passing user as your CustomUser instead of ApplicationUser and maybe int if your CustomKey is an int instead of string. (CreateAsync can infer types, I posted there to show them explicitly)
The other problem I see is you are not awaiting the task, you must also add await like :
IdentityResult user = await UserManager.CreateAsync(user, "abcwq12312!P");
Hope this helps.
EDIT:
For completeness I will post the full answer from this question but there is your answer. : Unable to access CreateAsync in User Manager
var result = await UserManager.CreateAsync(user, register.Password);
The UserManager in the above statement is not a Class as I've
expected. Its a property of type UserManager<ApplicationUser>.
So, at the beginning just declared a property as
public UserManager<ApplicationUser> UserManager { get; private set; }
And now I can use the Async version for creating users. The
following statement works.
var result = await UserManager.CreateAsync(user, register.Password);
I will also flag for possible duplicate.

Categories