.NET Core 2.1 Identity get all users with their associated roles
I have now implemented the following solution.
As CodeNotFound pointed out in the comments, IdentityUser used to have a Roles
property. This is no longer the case in .NET Core. This comment/issue on GitHub seems to be the current solution for .Net Core. I have attempted to implemented it with the following code:
ApplicationUser
public class ApplicationUser : IdentityUser
{
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
ApplicationUserRole
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
ApplicationRole
public class ApplicationRole : IdentityRole
{
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
DBContext
public class ApplicationDbContext
: IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserClaim<string>,
ApplicationUserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUserRole>(userRole =>
{
userRole.HasKey(ur => new { ur.UserId, ur.RoleId });
userRole.HasOne(ur => ur.Role)
.WithMany(r => r.UserRoles)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
userRole.HasOne(ur => ur.User)
.WithMany(r => r.UserRoles)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
}
}
Startup
services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.Stores.MaxLengthForKeys = 128)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Finally, make sure when you're using it that you eagerly load the User's UserRoles, and then the UserRole's Role like so:
this.Users = userManager.Users.Include(u => u.UserRoles).ThenInclude(ur => ur.Role).ToList();
I had an issue where the Role
property of each UserRole
was null and this was resolved by adding in the .ThenInclude(ur => ur.Role)
part.
Microsoft doc on multi-level eager loading: https://learn.microsoft.com/en-us/ef/core/querying/related-data#including-multiple-levels
ASP Core 2.2 update
Inherent from IdentityUserRole<Guid>
not string
You may also need to remove the code in the ModelBuilder to get migrations working.
ASP.net Identity 2.1 Get all users with roles
The UserManager
stuff in Identity tends to confuse people. Ultimately, users are still just a DbSet
on your context, so you can use your context like querying for any other object:
var role = db.Roles.SingleOrDefault(m => m.Name == "role");
var usersInRole = db.Users.Where(m => m.Roles.Any(r => r.RoleId == role.Id));
EDIT Forgot that IdentityUser.Roles
references IdentityUserRole
instead of IdentityRole
directly. So you need to get the role first, and then use the role's id to query into your users.
ASP.NET Core 2.1 displaying users and roles
You put everything inside ApplicatonUser
class. First rearrange your model classes as follows:
public class ApplicationUser : IdentityUser
{
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
public class ApplicationRole : IdentityRole
{
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
Now UserRoles
is a collection, you cannot display this with @Html.DisplayFor(m => item.UserRoles)
, rather you have to iterate over this collection as follows:
@model List<ApplicationUser>
@{
foreach (ApplicationUser applicationUser in Model)
{
<div>UserName : @applicationUser.UserName</div>
<br/>
<h4>User's Role</h4>
<hr/>
foreach (ApplicationUserRole applicationUserRole in applicationUser.UserRoles)
{
<div>RoleName: @applicationUserRole.Role.Name</div>
}
}
}
ASP.NET Core 1.1 getting all users and their roles
You anwsered yourself but your solution is not perfect because it causes performance issue. You're executing one request to your database to query users then in your foreach
loop you execute a new query for each user to get their related roles which is really bad. If you've X user in your database you will end up using :
- One query to get users
- X queries to get each user's roles.
You can do better by including the related roles in one query like this:
foreach (var user in _userManager.Users.Include(u => u.Roles).ToList())
{
list.Add(new ApplicationUserListViewModel {
UserEmail = user.Email,
Roles = user.Roles
});
}
Or just this:
var users = _userManager.Users.Include(u => u.Roles)
.Select(u => new ApplicationUserListViewModel {
UserEmail = user.Email,
Roles = user.Roles
})
.ToList();
Update for ASP.NET Core Identity 2.x
This solution is not valid for ASP.NET Core Identity 2.x as IdentityUser
no longer contains a Roles
property. See this answer for ASP.NET Core Identity 2.x.
EF 2.1 get all users in roles
The easiest way to do this is by using the UserManager
which is provided by ASP.NET Identity.
private UserManager<ApplicationUser> _userManager;
public AccountController(UserManager<ApplicationUser> userManager) {
_userManager = userManager;
}
The UserManager
should automatically be registered in the DI container if you have set up Identity correctly in Startup.cs
Then all you need to do is call await _userManager.GetUsersInRoleAsync(rolename);
Get list of users with assigned roles in asp.net identity 2.0
Not an expert, but ...
There seemed to be no built in funcionality for this in Identity and I could not get it work from built in Roles also (it seems to not work with claims based Identity).
So I ended up doing something like this:
var users = context.Users
.Where(x => x.Roles.Select(y => y.Id).Contains(roleId))
.ToList();
x.Roles.Select(y => y.Id)
gets a list of all role ids foruser x
.Contains(roleId)
checks if this list of ids contains necessaryroleId
User List with Role in .Net Core 3.1 Identity
You need to customize Identity Model and add navigation properties.
public class ApplicationUser : IdentityUser
{
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}
and then add required configuration.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>(b =>
{
// Each User can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne(e => e.User)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
modelBuilder.Entity<ApplicationRole>(b =>
{
// Each Role can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne(e => e.Role)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
});
}
and then you can use joins to query users with roles. More information on adding navigation proprieties can be found here
Related Topics
Getting All File Names from a Folder Using C#
How to Marshal a Struct That Contains a Variable-Sized Array to C#
Webbrowser Control IE8 Compatibility Mode On/Off Switch
C# Webbrowser Control -- Get Document Elements After Ajax
MVC Which Submit Button Has Been Pressed
How to Use .Net Reflection to Check for Nullable Reference Type
Could Not Load File or Assembly System.Runtime.Compilerservices.Unsafe
Implement Validation for Wpf Textboxes
Query an Xdocument for Elements by Name at Any Depth
Pass C# String to C++ and Pass C++ Result (String, Char*.. Whatever) to C#
ASP.NET Core 6 How to Access Configuration During Startup
How to Override a Non-Virtual Method
Best Way to Determine If Two Path Reference to Same File in C#