3
votes

In my ASP.NET MVC 5 application I want to list the roles of a user. I downloaded some samples that seem to be broken. Basically I want both the role ID and role name of the roles of a selected user (not the current user!).

ApplicationUser.Roles gives me an IdentityUserRole object with only RoleId and UserId.

ApplicationDbContext.Roles gives me an IdentityRole with RoleId, RoleName etc. of ALL application roles.

So what I want is a result set with the intersection of both sets while retaining full role information so that I can use both its role ID and role name.

I tried Intersect() but that didn't work because both objects are of different type. I tried the dumb style of iterating but got an exception saying the DAta Reader was already active so I am stumped :(

I tried the following on LinQPad (with the appropriate conenctions and namespaces):

string UserName = "[email protected]";
ApplicationDbContext ctx = new ApplicationDbContext();
var allroles = ctx.Roles.OrderBy(r => r.Id);
allroles.Dump();    // dumps well, 6 roles

ApplicationUser user = ctx.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
var myroles = user.Roles;
myroles.Dump();     // dumps well, 3 roles

IEnumerable<IdentityRole> list = from roles in allroles
    join uroles in myroles
    on roles.Id equals uroles.RoleId
    select roles;
list.Dump();    // exception

And while the query seems to produce no error during execution, its dumping does regardless of whether I use Dump() or an explicit foreach (IdentityRole item in list). The error I get in this case is

"Unable to reate a constant value of type 'Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole'. Only primitive types or enumeration types are supported in this context".

4
Have you tried doing instead of select roles, select new IdentityRole { Id = role.Id, Name = role.Name }.ToList(); - grimurd
Would you mind marking one of the answers as accepted so SO won't bump this question to the front page? - Gert Arnold

4 Answers

1
votes

The only problem here is that you are not calling ToList() method which execute the query immediately (everything will be held in the memory).

For better understanding - ToList() method converts an IEnumerable<T> to a List<T>.

So, your code will look like this:

var allroles = ctx.Roles.OrderBy(r => r.Id).ToList();    
var myroles = user.Roles.ToList();
0
votes

You could use a combination of the two approaches you tried, where you get roles from the context that are present in the ApplicationUser's Roles property...

var roles = ApplicationDbContext.Roles
                                .Where(ar => 
                                       ApplicationUser.Roles
                                                      .Select(ur => 
                                                              ur.RoleId)
                                                      .Contains(ar.RoleId));
0
votes

You can do this way :

 var rolesList = allroles.ToList().Join(myroles.ToList(), 
                                        left => left.Id, 
                                        right => right.RoleId, 
                                        (left,right) => left);

This way it is working for me for different scenario.

0
votes

You're trying to join an in-memory list, myroles, with an IQueryable, allroles, which produces a new IQueryable: list. However, this new IQueryable is translated into SQL, so myroles must be translated into SQL as well. This is not supported for lists of non-primitive types.

The solution is to join two IQueryables:

var myroles = ctx.Users.Where(u => u.UserName == UserName).SelectMany(u => u.Roles);

var list = from role in allroles
           join urole in myroles
           on role.Id equals urole.RoleId
           select role;