Reputation: 3215
So I've been searching Google and SO. Feels like this question has been asked many times, but no answer has helped me but I feel like I'm getting close. However, I'm new to LINQ and Lambda and don't have the knowledge to do what I want.
User Roles
-----------------------------------------
John Admin
Jane Staff, HR, Payroll
MyCoolUserName User
I got pretty close from this post and this post. Here's what I got so far.
public class UsersViewModel {
[Display(Name = "User")]
public ApplicationUser User { get; set; }
[Display(Name = "Roles")]
public string Roles { get; set; }
}
This solution returns blanks for the roles, and I had to add this to my web.config
file: <roleManager enabled="true" />
public class UsersController : Controller {
public async Task Index() {
var allUsers = await db.Users.ToListAsync();
var users = new List();
foreach (var user in allUsers) {
String[] roles = Roles.GetRolesForUser(user.UserName);
users.Add(new UsersViewModel {User = u, Roles = String.Join(",", roles.ToArray())});
}
return View(users);
}
}
This solution returns one row per user per role, but only returns the RoleId
public class UsersController : Controller {
public async Task Index() {
var allUsers = await db.Users.ToListAsync();
var users = allUsers.Select(u => new UsersViewModel {User = u, Roles = String.Join(",", u.Roles.Select(r => r.RoleId))}).ToList();
return View(users);
}
}
Here's what I get for Trial #2 when I change RoleId
to RoleName
:
I can tell that in trial #2, u.Roles
is linked to the UserRoles
table. Logically, I know that what I want is to inner join the Roles
table and get the name there.
I hope someone can help me out? Thanks in advance. Sample Project
Upvotes: 5
Views: 2638
Reputation: 3215
Thanks to @Kienct89 and a discussion we had, I accidentally stumbled upon the answer myself. Here's what I got, and if anyone can or wants to improve on it, please do so.
e.g.: I don't know if it's better to get all the roles into a variable first and iterate over that, you see this in version 1, or not like in version 2.
public class UsersController : Controller {
public async Task Index() {
var allUsers = await db.Users.ToListAsync();
var users = allUsers.Select(u => new UsersViewModel {User = u, Roles = String.Join(",", db.Roles.Where(role => role.Users.Any(user => user.UserId == u.Id)).Select(r => r.Name))}).ToList();
return View(users);
}
}
public class UsersController : Controller {
public async Task Index() {
var allUsers = await db.Users.ToListAsync();
// set all roles to a variable, so that we don't hit the database for every user iteration
// is this true?
var allRoles = await db.Roles.ToListAsync();
var users = allUsers.Select(u => new UsersViewModel {User = u, Roles = String.Join(",", allRoles.Where(role => role.Users.Any(user => user.UserId == u.Id)).Select(r => r.Name))}).ToList();
return View(users);
}
}
I feel like version 2 is more efficient in that it will not hit the database to get the roles for every user. Instead it has the roles in a variable. I'm not sure if I am right, but I would like to be enlightened and learn from anyone who does know.
Upvotes: 1
Reputation: 4895
You don't need to perform join in your LINQ query because UserRole is just a navigation property of User (so they will be automatically map by EF). The only thing you need to remember is if you disable Lazy Loading, you need to call Include(entity) manually to load the entity into query.
Code sample:
In case you disable "Lazy Loading":
public class UsersController : Controller {
public async Task Index() {
var users = allUsers.Include(Roles).Select(u => new UserViewModel {UserName = u.UserName, Roles = String.Join(",", u.Roles.Select(r => r.RoleName))}).ToList();
return View(users);
}
}
In case you enable "Lazy Loading":
public class UsersController : Controller {
public async Task Index() {
var users = allUsers.Select(u => new UserViewModel {UserName = u.UserName, Roles = String.Join(",", u.Roles.Select(r => r.RoleName))}).ToList();
return View(users);
}
}
UPDATE: I haven't seen your model classes yet, but I think you have 3 model classes (Users, Roles, UserRoles) because User and Role have many-to-many relationships (as shown in your gif, Roles only have UserId & RoleId).
public class UsersController : Controller {
public async Task Index() {
var users = allUsers.Select(u => new UserViewModel {UserName = u.UserName, Roles = String.Join(",", u.UserRoles.Where(userRole => u.Roles.Select(r => r.RoleId).Contains(userRole.Id)).Select(userRole => userRole.Name)}).ToList();
return View(users);
}
}
Upvotes: 0