49

I'm trying to pull out all my Identity users and their associated roles for a user management admin page. I thought this would be reasonably easy but apparently not. I've tried following the following solution: https://stackoverflow.com/a/43562544/5392786 but it hasn't worked out so far.

Here is what I have so far:

ApplicationUser:

public class ApplicationUser : IdentityUser
{
    public List<IdentityUserRole<string>> Roles { get; set; }
}

DBContext

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

Startup Identity code

services.AddIdentity<ApplicationUser, IdentityRole>(options => options.Stores.MaxLengthForKeys = 128)
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

Razor Page where I want to display the list:

public class IndexModel : PageModel
{
    private readonly UserManager<ApplicationUser> userManager;

    public IndexModel(UserManager<ApplicationUser> userManager)
    {
        this.userManager = userManager;
    }

    public IEnumerable<ApplicationUser> Users { get; set; }

    public void OnGetAsync()
    {
        this.Users = userManager.Users.Include(u => u.Roles).ToList();
    }
}

I get the following error when calling userManager.Users.Include(u => u.Roles).ToList();:

MySql.Data.MySqlClient.MySqlException: 'Unknown column 'u.Roles.ApplicationUserId' in 'field list''

Andy Furniss
  • 3,256
  • 3
  • 22
  • 44
  • 1
    IdnetityUser already has Roles property. So why adding it again in a subclass `ApplicationUser`? – CodeNotFound Jun 23 '18 at 19:49
  • I don't see it. Trying to access from `UserManager.Users`... – Andy Furniss Jun 23 '18 at 19:58
  • @CodeNotFound IdentityUser in .NET Core 2.1 does not have a Roles property https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.identityuser?view=aspnetcore-2.1 – Andy Furniss Jun 23 '18 at 20:39
  • 1
    It seems they changed all in ASP.Net Core Identity :) I found a issue on [GitHub](https://github.com/aspnet/Identity/issues/1361) I think this [comment](https://github.com/aspnet/Identity/issues/1361#issuecomment-348863959) seems to be the best solution IMHO. So please add your answer if you resolve your problem :) – CodeNotFound Jun 23 '18 at 20:59
  • Also please feel free to edit my answer [here](https://stackoverflow.com/a/43562544/5392786) (it is the same link you put in your question) and point users to this question for ASP.Net Core 2.1 ;-) – CodeNotFound Jun 23 '18 at 21:05
  • Ha, I'd actually just been looking at that issue on GitHub too and have made some progress with it. See update above. I will indeed point users here on the other SO question once I get it finalised. – Andy Furniss Jun 23 '18 at 21:13
  • You could also take a look at [Identity Management](https://github.com/mguinness/IdentityManager) for ASP.NET Identity which may save you some time. – Mark G Jun 23 '18 at 21:57
  • 1
    @CodeNotFound Cracked it. Was missing an eager load call to the `Role` property of the `UserRole`. See my answer. – Andy Furniss Jun 23 '18 at 22:10
  • 1
    @CodeNotFound I also edited your answer in the other question to point users this way for .NET Core. – Andy Furniss Jun 23 '18 at 22:17

12 Answers12

92

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.

domshyra
  • 791
  • 3
  • 10
  • 24
Andy Furniss
  • 3,256
  • 3
  • 22
  • 44
  • What if i want one-one relation between ApplicationUser and ApplicationUserRole ? – Zubair Rana Aug 29 '18 at 11:43
  • 4
    to avoid error: "Cannot create a DbSet for 'IdentityUserRole' because this type is not included in the model for the context", I changed your code to: services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders() .AddUserStore, UserRole,IdentityUserLogin, IdentityUserToken, IdentityRoleClaim>>() .AddRoleStore>>(); – Cedric Arnould Sep 08 '18 at 04:48
  • This worked for me with no problem. You might have a problem if you try to change Default User to User or something like that. In that case, drop Db and restart. – Cubelaster Nov 13 '18 at 20:51
  • 2
    Hey @Andy, I'm getting `The entity type 'IdentityUserRole' requires a primary key to be defined.` with `Identity v2.2.0`. Any idea? – Muhammad Hannan Feb 02 '19 at 03:48
  • @MuhammadHannan Hmm, not sure. In my example I am using a custom model for the user role and setting the key like so: `userRole.HasKey(ur => new { ur.UserId, ur.RoleId });`. You seem to not be using a model for your user role. Have you included `base.OnModelCreating(builder);` at the start of your `OnModelCreating` method? – Andy Furniss Feb 02 '19 at 22:53
  • 6
    Yes, I did. I'm doing exactly the same as shown in your example. However not working for me for some reason. – Muhammad Hannan Feb 03 '19 at 16:33
  • It works great! But for some reason, when I serialize the object using JSON.Net I get a self referencing loop exception – juanora Feb 18 '19 at 10:35
  • @juanora You will do, that's just how EF works (you get reference loops between the different entities because they all reference each other), but you can tell JSON serializer to ignore reference loops like so: `JsonConvert.SerializeObject(user, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });` or you can add it globally with your `Startup.cs` like this: `services.AddMvc().AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; })` – Andy Furniss Feb 18 '19 at 10:59
  • @AndyFurniss I tried that, but still doesnt work. If you check on https://stackoverflow.com/questions/16949520/circular-reference-detected-exception-while-serializing-object-to-json they are saying that JSON.NET is supposed to work without additional configurations with EF Core (from my experience thats correct) – juanora Feb 19 '19 at 15:24
  • @juanora Are you able to provide a link to your code for me to look at? – Andy Furniss Feb 19 '19 at 16:01
  • 3
    Have the same problem as @MuhammadHannan with ```ASP.NET Core 2.2.2```. ```The entity type 'IdentityUserRole' requires a primary key to be defined```. Gave up now, cant figure out how to solve it. – Andriod Mar 04 '19 at 23:14
  • @Andriod, @MuhammadHannan if you're using a Guid (`ApplicationUser : IdentityUser`) then you need the base class to inherent from Guid instead of string – domshyra Mar 25 '19 at 17:51
  • @domshyra : Thanks for your input, thats not the case. I am using string: `public class User : IdentityUser` -> `public class IdentityUser : IdentityUser` – Andriod Mar 25 '19 at 18:28
  • with this solution, you will not be able to use UserManager, without extending the Identity classes. When you are customizing ASP.NET Core Identity, you should not use AddEntityFrameworkStores anymore. Because it will override all of your previous settings and customization to default Identity services. First you need to create new services with the following signatures: https://stackoverflow.com/questions/39548668/why-this-violates-the-constraint-of-type-parameter-tuser – Stephen Himes Jul 20 '19 at 19:16
  • I have updated an existing project (dotnet 2.1) with the code above and everything seems to work except that there is now a migration which removes the "ApplicationUserId" from the "AspNetUserRoles" table. This is the foreign key between UserRoles and Users, which is very odd. Possibly a bug in the tooling? – Neil Oct 02 '19 at 09:04
  • My ApplicationUserRole/AspNetUserRoles table had a **Discriminator** column added. System generated queries had **WHERE [a].[Discriminator] IN (N'ApplicationUserRole', N'IdentityUserRole'**, so I had to populated a **ApplicationUserRole** value in the **Discriminator** column. – Phil Huhn Oct 17 '19 at 13:33
  • So if we add ApplicationUserRole it will create a new table right? so what's the purpose of AspNetUserRoles table? – Kavin404 Dec 26 '19 at 18:49
  • This solution didn't work anymore with EF Core 3.1, an excpetion on the `HasKey` : `'A key cannot be configured on 'ApplicationUserRole' because it is a derived type` – Christophe Gigax Feb 13 '20 at 08:51
  • 2
    @ChristopheGigax Instead of `builder.Entity().HasKey(x => new { x.UserId, x.RoleId });` you need `builder.Entity>().HasKey(x => new { x.UserId, x.RoleId });` – parveenkhtkr Feb 18 '20 at 10:59
  • The same issue as @MuhammadHannan. I even tried the steps in official [documentation](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-3.1). I am using ASP.NET Core 3.1 with Identity 2.2. – chunk1ty Sep 09 '20 at 20:55
  • same issue as @MuhammadHannan. ASP.Net Core 3.1 and Microsoft.AspNetCore.Identity.EntityFrameworkCore 3.1.7 , when use Guid for UserId and RoleId, it doesn't work, error is like "The entity type 'ApplicationUserRole' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'." but actually in table userroles have already had key(UserId,RoleId). is it a bug? – Haiping Fan Nov 03 '20 at 08:11
  • It doesn't work in .Net Core 5.0. Everything compiles but .net adds additional columns to `AspNetUserRoles` with names `UserId1` and `RoleId1` thus roles are not visible in user instance. I really prefer this solution but I vote for `-1` until this issue will be fixed. It's just because I spent 3 hours checking if it works. – LukaszTaraszka Feb 19 '21 at 22:29
8

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

  • 1
    The answer is fine, thank you, and so is the explanation. If other users don't like the way it is phrased they're welcome to edit it. – nico_c May 11 '19 at 16:47
  • This approach appears a bit more pragmatic than the one of CodeNotFound. Thank you Mohamed. – Charles de M. Aug 12 '19 at 13:00
  • You Welcome Sir> – Mohamed Aboughoufa Aug 19 '19 at 12:12
  • @MarkRotteveel, don't harsh people new to the community for answering. The Answer is self-explanatory, and if you feel that there is something missing, just edit it. By the way, this approach isn't appropriate as this would make multiple request to DB to get the data for each user, rather then getting the mapped data from DB for which normally you would use joins on tables. Problem with this is that, rather then processing on DB server, every thing is being done on App server. In scenario when DB is distributed on multiple servers, it would result in higher latency. – AbhiAbzs Apr 27 '20 at 18:21
6

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"
jstur
  • 601
  • 5
  • 9
5

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.

3

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.

GHayes
  • 336
  • 5
  • 8
  • 2
    This query failed with `Processing of the LINQ expression [...] by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core` – Christophe Gigax Feb 13 '20 at 09:16
2

I implemented a solution to this problem providing a balance between performance and complexity I was happy with. We perform a handful of database roundtrips, one for every role, rather than one for every user. No DbMigrations or class overrides required.

        //Fetch all the Users
        var users = await userManager.Users
            .Select(u => new { User = u, Roles = new List<string>() })
            .ToListAsync();

        //Fetch all the Roles
        var roleNames = await roleManager.Roles.Select(r => r.Name).ToListAsync();

        foreach (var roleName in roleNames)
        {
            //For each role, fetch the users
            var usersInRole = await userManager.GetUsersInRoleAsync(roleName);

            //Populate the roles for each user in memory
            var toUpdate = users.Where(u => usersInRole.Any(ur => ur.Id == u.User.Id));
            foreach (var user in toUpdate)
            {
                user.Roles.Add(roleName);
            }
        }
N1njaB0b
  • 658
  • 6
  • 11
2

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());
    }
}

1

The accepted answer required customization of identity by extension, which without this will disable the use of roleManager and userManager. When you are customizing ASP.NET Core Identity, you should not use AddEntityFrameworkStores anymore. Because it will override all of your previous settings and customization to default Identity services. First you need to create new services with the following signatures: Why this violates the constraint of type parameter 'TUser'?

Without extending, using userManager and roleManager:

namespace identityDemo.Controllers
{
    public class UserManagementController : Controller
    {
        private readonly ApplicationDbContext _context;
        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly UserManager<IdentityUser> _userManager;

            public UserManagementController(ApplicationDbContext context, 
UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager)
        {
            _context = context;
            _roleManager = roleManager; 
            _userManager = userManager; 
        }

        // GET: ApplicationUserRoles
        public async Task<IActionResult> GetApplicationUsersAndRoles()
        {
            return View(new UserMv(
                (from user in await _userManager.Users.ToListAsync()
                 select new UserMv(user, GetUserRoles(user).Result)).ToList()));
        }

        private async Task<List<string>> GetUserRoles(IdentityUser user)
        {
            return new List<string>(await _userManager.GetRolesAsync(user));
        }
}

With simple constructor for mapping to DTO:

namespace IdentityDemo.Models.ModelView
{
    public class UserMv
    {
public UserMv(IdentityUser aus, List<string> userRoles)
        {
            UserId = aus.Id;
            UserName = aus.UserName;
            RolesHeld = userRoles; 
            Email = aus.Email;
            EmailConfirmed = aus.EmailConfirmed;
            LockoutEnabled = aus.LockoutEnabled;
            AccessFailedCount = aus.AccessFailedCount;
        }
}

and the startup.cs

services.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Stephen Himes
  • 477
  • 5
  • 9
0

Worked perfectly. I'm using integer keys, so I replaced the "string" with "int"

ApplicationRole : IdentityRole<int>
ApplicationUserRole : IdentityUserRole<int>
ApplicationUser : IdentityUser<int>

ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, 
IdentityUserClaim<int>,
ApplicationUserRole, IdentityUserLogin<int>, IdentityRoleClaim<int>, 
IdentityUserToken<int>>

Linq: RoleId = (from a in m.UserRoles select a.Role.Id).FirstOrDefault(),

0

ASP.NET CORE 3.1 UPDATE

I use the following code and it works perfectly

  namespace MyProject.Pages.Roles
{
    public class DetailsModel : PageModel
    {

        public UserManager<ApplicationUser> _userManager;
        public RoleManager<IdentityRole> _roleManager;
        private readonly ApplicationDbContext _context;

        public DetailsModel(UserManager<ApplicationUser> userManager,
            RoleManager<IdentityRole> roleManager,
            ApplicationDbContext context)
        {
            _userManager = userManager;
            _roleManager = roleManager;
            _context = context;
        }

        public IList<IdentityRole> Roles { get; set; }

        [BindProperty]
        public IList<ApplicationUser> applicationUserList { get; set; }

        [BindProperty]
        public IList<IdentityRole> allRolesList { get; set; }

        public IList<IdentityUserRole<string>> usersRoles { get; set; }
        public IList<IdentityUserRole<string>> usersRole { get; set; }
        public IList<IdentityUserRole<string>> userWithRole { get; set; }


        public Dictionary<ApplicationUser, string> itemDictionary;

        public async Task<IActionResult> OnGetAsync(string id)
        {
            if (id == null)
            {
                return NotFound();
            }


            Roles = await _context.Roles.Where(r => r.Id == id).ToListAsync();

            allRolesList = await _context.Roles.ToListAsync();

            usersRoles = await _context.UserRoles.ToListAsync();
            usersRole = await _context.UserRoles.Where(r => r.RoleId == id).ToListAsync();
            userWithRole = usersRoles.Where(u => u.RoleId == id).ToList();

            applicationUserList = await _context.Users.ToListAsync();

            itemDictionary = new Dictionary<ApplicationUser, string> { };

            foreach (var item in usersRole)
            {
                itemDictionary.Add(await _context.Users.FindAsync(id = item.UserId), item.UserId);
            }

            return Page();
        }
    }
}

It's very useful to bind all that stuff to get an idea what's going on!

On the Details Razor Page I simply have

    @page "{id}"
@model MyProject.Pages.Roles.DetailsModel
@{
    Layout = "~/Views/Shared/_Layout.cshtml";
    var dict = Model.itemDictionary;
    int cou = dict.Count();
    var x = Model.applicationUserList;
}

<h5 class="bg-primary text-white text-center p-2">List of Members having the role @Model.Roles[0].Name</h5>
<table class="table">
    <thead>
        <tr>
            <th>@Html.DisplayNameFor(model => model.userWithRole[0].UserId)</th>
            <th>@Html.DisplayNameFor(model => model.userWithRole[0].RoleId)</th>
            <th>LastName, FirstName</th>
        </tr>
    </thead>

    <tbody>

        @foreach (var kvp in dict.ToArray())
        {
            <tr>
                <td>@kvp.Key</td>
                <td>@kvp.Value</td>
                <td>@kvp.Key.LastName, @kvp.Key.FirstName</td>
            </tr>
        }

    </tbody>
</table>

And here the result:

enter image description here

Code4Fun
  • 163
  • 1
  • 14
-1

I needed to display all the roles a user has a in a view, instead of the solutions provided here already, i went with this quick and dirty thing:

@foreach(var user in Model.Users)
        {
        <tr>
            <td>@user.Email</td>
            <td>@String.Join(", ", @Model._userManager.GetRolesAsync(user).GetAwaiter().GetResult().ToArray())</td>
        </tr>
        }

_userManager has to be public for this to work. and user is simply an instance of IdentityUser.

  • 2
    Please never do this. – Jammer Apr 05 '20 at 10:08
  • Could you elaborate? – Andrius Svylas Apr 22 '20 at 10:14
  • You have the _userManager being used right in a razor view. This kind of thing should be encapsulated in a service and you pass objects to the view to render. – Jammer Apr 22 '20 at 11:49
  • Well that's a valid point, but it has nothing to do with displaying identity roles in a view, I'd even argue that this is clearer for someone inexperienced than some ambiguous property called userArray. A lot of people aren't smart, myself included and every little bit extra in an answer can help in finding a solution easier. – Andrius Svylas Apr 22 '20 at 14:39
  • Another problem is that `GetRolesAsync` might query the database for every user. You should generally avoid calling databases in a loop. We once had a case where a developer who did this caused 5000 queries for one page. Performance was not that great... – Arikael Sep 01 '20 at 06:21
-4

I solved this by creating a view with all the columns I needed (including roles) and adding it to the context.

Dave Shinkle
  • 807
  • 9
  • 10