Show activity on this post. var user = await _userManager. FindByIdAsync(UserId); var roles = await _userManager. GetRolesAsync(user); return OK(new { User = user, Roles = roles });
The following code example uses the GetUsersInRole method to get a list of the users in a particular role and binds the results to a GridView control.
ASP.NET Core Identity: Is an API that supports user interface (UI) login functionality. Manages users, passwords, profile data, roles, claims, tokens, email confirmation, and more.
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://docs.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.
For dotnet core 3.1, I've been using the following general approach.
// _appContext is an instance of IdentityDbContext<ApplicationUser>
_appContext.Users
.SelectMany(
// -- below emulates a left outer join, as it returns DefaultIfEmpty in the collectionSelector
user => _appContext.UserRoles.Where(userRoleMapEntry => user.Id == userRoleMapEntry.UserId).DefaultIfEmpty(),
(user, roleMapEntry) => new { User = user, RoleMapEntry = roleMapEntry })
.SelectMany(
// perform the same operation to convert role IDs from the role map entry to roles
x => _appContext.Roles.Where(role => role.Id == x.RoleMapEntry.RoleId).DefaultIfEmpty(),
(x, role) => new {User = x.User, Role = role})
.ToList() // runs the queries and sends us back into EF Core LINQ world
.Aggregate(
new Dictionary<ApplicationUser, List<IdentityRole>>(), // seed
(dict, data) => {
// safely ensure the user entry is configured
dict.TryAdd(data.User, new List<IdentityRole>());
if (null != data.Role)
{
dict[data.User].Add(data.Role);
}
return dict;
},
x => x);
The SQL this generates is straightforward and reasonable:
SELECT "a"."Id",
"a"."AccessFailedCount",
"a"."ConcurrencyStamp",
"a"."Email",
"a"."EmailConfirmed",
"a"."LockoutEnabled",
"a"."LockoutEnd",
"a"."NormalizedEmail",
"a"."NormalizedUserName",
"a"."PasswordHash",
"a"."PhoneNumber",
"a"."PhoneNumberConfirmed",
"a"."SecurityStamp",
"a"."TwoFactorEnabled",
"a"."UserName",
"a1"."Id",
"a1"."ConcurrencyStamp",
"a1"."Name",
"a1"."NormalizedName"
FROM "AspNetUsers" AS "a"
LEFT JOIN "AspNetUserRoles" AS "a0" ON "a"."Id" = "a0"."UserId"
LEFT JOIN "AspNetRoles" AS "a1" ON "a0"."RoleId" = "a1"."Id"
loops through user list and get user roles by calling _userManager.GetRolesAsync(user) function and loops through roles of user and split roles with "," in one string variable
[HttpPost]
public async Task<IActionResult> OnPostGetPagination()
{
var users = await _userManager.Users.ToListAsync();
InputModel inputModel = new InputModel();
foreach (var v in users)
{
inputModel = new InputModel();
var roles = await _userManager.GetRolesAsync(v);
inputModel.Email = v.UserName;
inputModel.role = "";
foreach (var r in roles)
{
if (!inputModel.role.Contains(","))
{
inputModel.role = r;
}
else
{
inputModel.role = "," + r;
}
}
Input2.Add(inputModel);
}
}
good luck
Reference comment
First is the code to get data
public async Task<IEnumerable<AccountViewModel>> GetUserList()
{
var userList = await (from user in _context.Users
select new
{
UserId = user.Id,
Username = user.UserName,
user.Email,
user.EmailConfirmed,
RoleNames = (from userRole in user.Roles //[AspNetUserRoles]
join role in _context.Roles //[AspNetRoles]//
on userRole.RoleId
equals role.Id
select role.Name).ToList()
}).ToListAsync();
var userListVm = userList.Select(p => new AccountViewModel
{
UserId = p.UserId,
UserName = p.Username,
Email = p.Email,
Roles = string.Join(",", p.RoleNames),
EmailConfirmed = p.EmailConfirmed.ToString()
});
return userListVm;
}
In ASP.Net core 2.1 we to setup ApplicationRole like this in order to get Roles of users. You need to defined data you want explicit expose for user to use
public class ApplicationRole : IdentityRole
{
public virtual ICollection<IdentityUserRole<string>> Users { get; set; }
public virtual ICollection<IdentityRoleClaim<string>> Claims { get; set; }
}
Finally
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
modelBuilder.Entity<User>().HasMany(u => u.Claims).WithOne().HasForeignKey(c => c.UserId).IsRequired().OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<User>().HasMany(u => u.Roles).WithOne().HasForeignKey(r => r.UserId).IsRequired().OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ApplicationRole>().HasMany(r => r.Claims).WithOne().HasForeignKey(c => c.RoleId).IsRequired().OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ApplicationRole>().HasMany(r => r.Users).WithOne().HasForeignKey(r => r.RoleId).IsRequired().OnDelete(DeleteBehavior.Cascade);
modelBuilder.EnableAutoHistory(null);
}
The result will be the user name and user roles. If user have more than 1 roles the data will display like this Admin, Editor, etc...
Full code can be found here here here and here Hope this help.
Since this is the top google search result; Nowadays you can just join off the UserRoles dbset (if your db context inherits from IdentityDbContext).
E.g outer joining the roles table to any user roles and then creating our manageUserModel (reduced info of applicationUser class for our api):
var employees = (from bb in _appContext.Users
join roleIds in _appContext.UserRoles on bb.Id equals roleIds.UserId
join role in _appContext.Roles on roleIds.RoleId equals role.Id into roles
orderby bb.LastName, bb.FirstName
where roles !=null && roles.Any(e => e.Name == Permissions.RoleNames.Administrator || e.Name == Permissions.RoleNames.Employee)
select ManageUserModel.FromInfo(bb, roles)).ToList();
public static ManageUserModel FromInfo(ApplicationUser info, IEnumerable<UserRole> roles)
{
var ret= FromInfo(info);
ret.Roles = roles.Select(e => new SimpleEntityString() {Id=e.Id, Text=e.Name}).ToList();
return ret;
}
This also demos a where clause using any of the role info (the above selects only users in our Admin and Employee roles).
Note: this inner joins the IdentityUserRole, so only users with a role will be returned, if you want all users just add a "into identRoles" to the end of the join roleIds... line and modify the rest of the conditions accordingly.
You can use EF Core 5.0 Many-To-Many feature, and avoid subclassing IdentityUserRole/IdentityRole.
ApplicationUser
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;
public class ApplicationUser : IdentityUser
{
public ICollection<IdentityRole> Roles { get; set; }
}
DbContext:
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
...
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>()
.HasMany(u => u.Roles)
.WithMany("Users")
.UsingEntity<IdentityUserRole<string>>(
userRole => userRole.HasOne<IdentityRole>()
.WithMany()
.HasForeignKey(ur => ur.RoleId)
.IsRequired(),
userRole => userRole.HasOne<ApplicationUser>()
.WithMany()
.HasForeignKey(ur => ur.UserId)
.IsRequired());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With