6

Using ASP.NET Core identity, I create a new user with UserManager.CreateAsync() and assign them to an existing role with UserManager.AddToRoleAsync. This works, as the realtion between user and role is stored in the AspNetUserRoles table of the database.

But when I fetch the user using the UserManager (e.g. UserManager.FindByMail() method) then the Role list is empty. I also tried the Include function from EF like this:

var user = userManager.Users.Include(u => u.Roles).FirstOrDefault(u => u.Email == "test@test.de");

This gave me the Ids of the n:m association table, which is not very usefull as I need the role names. Loading them using a second query is also not possible, since the Roles attribute of the identity user is readonly. I would expect to get a List<string> of the role-names. Couldn't find any information about this.

For me the only workaround seems to add a custom attribute to my user and fill them with the data, which I fetch using a second query. But thats not a nice solution. Cant belive that ASP.NET Core Identity has no better way of getting those data...

Tseng
  • 61,549
  • 15
  • 193
  • 205
Lion
  • 16,606
  • 23
  • 86
  • 148

2 Answers2

13

UserManager does not load the relations by default.

The manual inclusion is a good way but as stated here - direct M:N relationships are not yet supported by EntityFramework Core.

So there are two ways I see:

(The preffered one) Use

    userManager.GetRolesAsync(user);

This will return a List<string> with user's role names. Or use some EF query to get an IdentityRole objects by joined IdentityUserRole. Unfortunatelly, this requires an acceptance with the fact the roles will not be directly in the User entity.

OR you can implement custom IdentityUserRole, create a relation to IdentityRole there and then query it with `

Include(user => user.Roles).ThenInclude(role => role.Role)

How to implement own Identity entities is described e.g. here. But it's complicated approach and the Role objects will be nested in the binding entities.


However, you can declare a property in your ApplicationUser:

[NotMapped]
public List<string> RoleNames {get; set;}

and use it at you free will...

Community
  • 1
  • 1
rudolfdobias
  • 1,778
  • 3
  • 17
  • 40
  • I override IdentityUserRole (and all other Identity classes) and then use .Include(u => u.Roles).ThenInclude(r => r.Role) but I get error Invalid column name 'AppRoleId1' when getting result from DB. Any idea? – Makla Feb 22 '17 at 08:37
  • 1
    @Makla: The col name 'AppRoleId1' looks weird. Did you generate a migration for your changes? If yes, does it look ok? – rudolfdobias Feb 22 '17 at 13:56
  • The error went away, but Role property is null: http://stackoverflow.com/questions/42388369/entity-framework-asp-net-core-1-list-all-users-with-role-names – Makla Feb 22 '17 at 15:28
-2

Here is the solution I came up with. It may not be the most efficient. Hope it helps someone.

public class UserVM
{
    public string Id { get; set; }
    public string UserName { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Roles { get; set; }
}

// Project each element of query into List<UserVM>
var list = (from user in _userManager.Users
            select new UserVM
            {
                Id = user.Id,
                UserName = user.UserName,
                Name = user.Name,
                Email = user.Email
            }).ToList();

list.Select(async user =>
{
    var userEntity = await _userManager.FindByIdAsync(user.Id);
    user.Roles = string.Join("; ", await _userManager.GetRolesAsync(userEntity));
}).ToList();
codecypher
  • 837
  • 9
  • 14
  • This solution will for sure will consume resources especially with back and forth to sql server to look for every user role. Let's say we have 50k users, each time you load this will make 50k requests to sql server which will get it down anyways. – NiL Sep 13 '19 at 15:40
  • Please do not query database in loops.. – Teoman Tıngır Jan 29 '23 at 18:13