.Net Core 2.1 Identity Get All Users with Their Associated Roles

.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 for user x
  • .Contains(roleId) checks if this list of ids contains necessary roleId

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



Leave a reply



Submit