0

I have a bog-standard implementation of Users and Roles using ASP.NET Identity:

public class User : IdentityUserBase { }
public class Role : IdentityRoleBase { }
public class UserRole : IdentityUserRoleBase { }

I need a property or method in the User class that returns the (first) role of the user (in my app, each user can only have one):

public class User : IdentityUserBase
{
  // ...
  public Role GetRole()
  {
    return this.Roles.FirstOrDefault(); // this actually returns a UserRole object, with only a UserId and a RoleId available (no Role object)
  }
}

Is there a way to do this here in the DAL?

I know it's possible at the UI layer using global objects like RoleManager, DbContext or HTTPContext but I don't have access to those in the User class (and don't want to have to pass them in as arguments).

I would have thought that this must be a standard use-case but I can't find an answer ... my question is basically the same as this one but I need access to the Role information within the User object itself.

Ed Graham
  • 4,306
  • 3
  • 30
  • 30
  • 1
    This problem sounds more like a bad design decisions. a User class should not know *how to get data*, it should be populated at the time of instantiation by whatever factory created it. You should be inverting control.. IE `RoleManager.PopulateRoleFor(user);` makes way more sense. – Erik Philips Feb 13 '18 at 17:13
  • The class doesn't need to know _how_ to get the data; it just needs to get it. I need a quick little method or property that tells me what the user's role is - nice and simple. Your design, whilst undoubtedly more modern, makes far less sense to me ... – Ed Graham Feb 13 '18 at 19:22
  • @EdGraham is this for any user or for user who is executing the current request? – trailmax Feb 13 '18 at 23:30
  • @trailmax this is for any user. I want to loop through the users and determine the (first) role for each one. And I think I've just found the answer ... – Ed Graham Feb 14 '18 at 09:37

2 Answers2

1

First of all, you shouldn't put the GetRole method in the User class, keep those models simple and clean.

To get the roles for a user, you need the db context and the user ID, and you can do this:

var userId = ...; //Get the User ID from somewhere

var roles = context.Roles
    .Where(r => r.Users.Any(u => u.UserId == userId))
    .ToList();
DavidG
  • 113,891
  • 12
  • 217
  • 223
  • Thanks David; but I need a way of interrogating the User object in order to find out the role of the user I'm looking at. I don't have a reference to the global context object in the User class. – Ed Graham Feb 13 '18 at 16:32
  • Well you can't. The user object will only contain `IdentityUserRole` objects, and even then it's not guaranteed they will be included if your query didn't request them. Remember this stuff is coming from a database, you can't just magic the data out of thin air :) – DavidG Feb 13 '18 at 16:34
  • 2
    This is why I said you shouldn't pollute your user class with functions like this. – DavidG Feb 13 '18 at 16:36
  • Thanks for your answer, David. I would have thought that being able to interrogate a User object to find out its role was not polluting my user class but making it functionally useful. In other Code-First EF projects, I can do the equivalent of this for other classes by setting navigation properties; I don't quite understand why this can't be done easily with ASP.NET Identity. – Ed Graham Feb 13 '18 at 16:39
  • Roles are still just another navigation class though, the only difference is that they're already set up for you. – DavidG Feb 13 '18 at 16:40
  • So do you think I could be able to do this by setting navigation properties in the OnModelCreating(DbModelBuilder modelBuilder) function? I was wary of doing this because I didn't want to interfere with what ASP.NET Identity came with out of the box. – Ed Graham Feb 13 '18 at 16:47
  • But even with the navigation properties, you still need an active db context or it won't work. So there's really no point messing with the Identity classes – DavidG Feb 13 '18 at 16:51
0

This worked:

public class UserRole : IdentityUserRoleBase
{
    public virtual Role Role { get; set; } // add this to see roles
    public virtual User User { get; set; } // add this to see users
}

The User and Role properties are now exposed through the UserRole property that is available through the User object.

Looking back through the evolution of ASP.NET Identity here, these properties were present in IdentityUserRole by default in version 1 but then removed in version 2 in order to support generic primary keys, with a recommendation to use the UserManager class. However, this doesn't help cases like mine where the operations need to be done in the DAL and don't have easy access to this class (or the any of the context classes). Given that I will never have any other type of primary key, it feels quite safe to do this.

Ed Graham
  • 4,306
  • 3
  • 30
  • 30