Get role list with associated users in ASP.NET Identity - c#

I have a role. How can I find the list of users which have that role?
public ViewResult Index()
{
return View(roleManager.RoleList.ToList());
}
In this method I take the list of roles there have the user's UsersId. Now how to link it with my UserModel to take the UserName?
I am not so good in the LINQ, and can't find a good idea
In the result I want to make a table in the view
foreach (RoleModel role in Model)
{
<tr>
<td>#role.Id</td>
<td>#role.Name</td>
<td>#role.Description</td>
<td>
#if (role.Users == null || role.Users.Count == 0)
{
#: Нет пользователей в этой роли
}
else
{
//User name which have that role
}
</td>
</tr>
}

This is one of the miss design of ASP.NET Identity that there is no short cut way of getting the list of Roles with its associated Users. But you can get with some extra effort as follow:
public class RoleViewModel
{
public string RoleId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<UserViewModel> Users { get; set; }
}
public class UserViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
}
public ViewResult Index()
{
List<RoleViewModel> rolesWithUsers = new List<RoleViewModel>();
List<ApplicationRole> applicationRoles = RoleManager.Roles.Include(r => r.Users).ToList();
foreach (ApplicationRole applicationRole in applicationRoles)
{
RoleViewModel roleViewModel = new RoleViewModel()
{
RoleId = applicationRole.Id,
Name = applicationRole.Name,
Description = applicationRole.Description
};
List<UserViewModel> usersByRole = UserManager.Users.Where(u => u.Roles.Any(r => r.RoleId == applicationRole.Id))
.Select(u => new UserViewModel
{
UserId = u.Id,
UserName = u.UserName
}).ToList();
roleViewModel.Users = usersByRole;
rolesWithUsers.Add(roleViewModel);
}
return View(rolesWithUsers);
}
Now each role will have its associated users.
Note : From performance standpoint, above solution is not a good option. That's why its always better to use ASP.NET identity entities with your own DbContext instead of default IdenityStote.

Related

ApplicationUser.Username is null even though the ApplicationUserId is not

I'm currently facing a problem with a NullException. I'm getting the error at #comment.ApplicationUser.UserName down below, but only if I am accesing a comment which was posted by another user rather than the one who uploaded the picture. If I comment to the same picture the user(me) posted it shows the UserName correctly. I don't really know what could be the problem, since if I call #comment.ApplicationUserId everything works as it should displaying the id of the user who posted the image. Shouldn't it be linked automatically to the ApplicationUser object when I call #comment.ApplicationUser.UserName ?
The view where I'm getting the error
#foreach (var comment in Model.Comments)
{
<dd class="font-italic">
#comment.Body commented by #comment.ApplicationUser.UserName
</dd>
}
The Comment Model
public class Comment
{
public int Id { get; set; }
public string Body { get; set; }
public bool ApprovedByUser { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public string ApplicationUserId { get; set; }
public Photo Photo { get; set; }
public int PhotoId { get; set; }
}
Every-time I create a new comment into the CommentsController/Create I do this
public ActionResult Create([Bind(Include = "Id,Body,ApprovedByUser,ApplicationUserId,PhotoId")] Comment comment, int id)
{
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
var currentUser = userManager.FindById(User.Identity.GetUserId());
comment.PhotoId = id;
comment.ApplicationUserId = currentUser.Id;
if (ModelState.IsValid)
{
db.Comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.PhotoId = new SelectList(db.Photos, "Id", "Title", comment.PhotoId);
return View(comment);
}
I also have a custom PhotoCommentsViewModel containing the photo and the comments for that photo.
public class PhotoCommentsViewModel
{
public Photo Photo { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
I get the abobe viewModel from Photo/Details/{id} controller here
public ActionResult Details(int? id)
{
var photo = db.Photos.Find(id);
var comments = db.Comments.Where(s => s.PhotoId == photo.Id);
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var viewModel = new PhotoUserViewModel
{
Photo = photo,
Comments = comments,
};
if (viewModel.Photo == null)
{
return HttpNotFound();
}
return View(viewModel);
}
You should use eager loading in this case.
var comments = db.Comments
.Where(s => s.PhotoId == photo.Id)
.Include(x =>x.ApplicationUser).ToList();
see this
In the Details action, ApplicationUser needs to be eagerly loaded with the comments.
var comments = db.Comments.Where(s => s.PhotoId == photo.Id).Include(x => x.ApplicationUser).ToList();
Further reading on Loading Related Entities

Dynamically generate columns with check box for each row in ASP.Net MVC

I am working two database tables where UserDetaisl table holds details about Users having one or multiple Roles and Role table holds name of the Roles. I need to generate a HTML table something similar to the picture below. Where Each role from the Role table would be shown as a column and user with assigned role would be displayed as a check box ticked for that cell. non assigned role would show unticked check box. One user can have more than one role.
My classes look like:
public partial class UserDetails
{
public string username { get; set; }
public string role { get; set; }
public virtual Role Role1 { get; set; }
}
public partial class Role
{
public string Role1 { get; set; }
public virtual ICollection<UserDetails> UserDetails { get; set; }
}
Thought of creating a ViewModel which would show the Role selection.
public class RoleSelectionViewModel
{
public bool Selected { get; set; }
public bool NotSelected { get; set; }
public virtual Role Role1 { get; set; }
}
Not sure how to put all these toegther to generate the columns on the fly.
currentlty the following Controller method only lists all the users from the table with associated role.
public ActionResult Index()
{
var userDetails = db.UserDetails.Include(u => u.Role1);
return View(userDetails .ToList());
}
Your UserDetails class contains properties string role and virtual Role Role1 which means that a UserDetails can only contain one role. In order to select and assign multiple roles, then you model would need a public virtual ICollection<Role> Roles { get; set; } property.
In order to generate the view you want, you need to create view models
public class UserVM
{
public string UserName { get; set; }
public List<RoleVM> Roles { get; set; }
}
public class RoleVM
{
public string RoleName { get; set; }
public bool IsSelected { get; set; }
}
and your view would be
#model List<UserVM>
....
#using (Html.BeginForm())
{
<table>
<thead>....</thead>
<tbody>
#for(int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(m => m[i].UserName)
#Html.HiddenFor(m => m[i].UserName)
</td>
#for(int j = 0; j < Model[i].Roles.Count; j++)
{
<td>
#Html.HiddenFor(m => m[i]Roles[j].RoleName)
#Html.CheckBoxFor(m => m[i]Roles[j].IsSelected)
</td>
}
</tr>
}
</tbody>
</table>
<input type="submit" ... />
}
Since the models you have shown are not correct for what your wanting to achieve, and I suspect you will need to makes changes to you data base (you will need tables for Users, Roles and UserRoles), the following controller code is a suggestion of what it should look like
public ActionResult Edit()
{
var roles = db.Roles; // get the collection of all roles
var users = db.Users.Include(x => x.Roles); // get the collection of all users including their selected roles
List<UserVM> model = new List<UserVM>();
foreach (var user in users)
{
UserVM u = new UserVM
{
UserName = user.username,
Roles = roles.Select(x => new RoleVM
{
RoleName = x.Role1
}).ToList()
}
.... // set the IsSelected property of each RoleVM based existing roles
model.Add(u);
}
return View(model);
}
[HttpPost]
public ActionResult Edit(List<UserVM> model)
{
// Get the selected roles for each user
foreach (UserVM user in Model)
{
var selectedRoles = user.Roles.Where(x =>x.IsSelected);
If you want the Users to have several Roles you will have to update your UserDetails class to have a Collection of Roles instead of just one.
Then update your RoleSelectionViewModel like
public class RoleSelectionViewModel
{
public ICollection<Role> Roles { get; set; } //list of all roles existing in your db for the columns
public ICollection<UserDetails> Users {get; set;} //list of all users for which you want to set roles
}
and then in the controller
public ActionResult Index()
{
var userDetails = db.UserDetails.Include(u => u.Roles).ToList();
var allRoles = db.Roles.ToList();
var viewModel = new RoleSelectionViewModel();
viewModel.Users = userDetails;
viewModel.Roles = allRoles;
return View(viewModel);
}
then you can Iterate over the Roles for the columns and over the users for the Rows and set the Checkboxes if the user has the role of the actual column.

can't get values in foreach from Viewbag mvc

I'm trying to get values from userList viewbag.i can't figure out the solution. Error is:
An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll but was not handled in user code
Additional information: 'object' does not contain a definition for 'name'
though in ViewBag.userList contains data (2 objects) which i can see while debugging
#foreach (var aUser in ViewBag.userList)
{
<tr>
<td>#aUser.name</td>
<td>#aUser.username</td>
.....
<td>#Html.ActionLink("Edit", "UserEdit","Users")</td>
<td>#Html.ActionLink("Delete", "UserDelete", "Users")</td>
</tr>
}
I have a superclass and a childclass
superclass
public partial class user
{
public int id { get; set; }
public string name { get; set; }
public string username { get; set; }
...
public string user_role { get; set; }
}
childclass
public class UserSub: user
{
public string CreatedUserName { get; set; }
public string ModifiedUserName { get; set; }
}
In my controller i used linq to get values from database and stored it to Viewbag.userList. My controller function is
public ActionResult UserList()
{
IEnumerable<user> users = null;
users = dbEntities.users.ToList();
if (users != null)
{
var userLists = (from a in users join b in users on a.created_user_id equals b.id select new { a.name, a.username, a.password, a.user_role, a.is_enable, a.is_approved, CreatedUserName = b.name, a.create_time, a.is_android, a.device_id }).ToList();
ViewBag.userList = userLists;
}
return View();
}
tried List<UserSub> users=ViewBag.userList....getting error too
Use a ViewModel to share data between view and controller.
For example, first create the ViewModel:
public class userViewModel{
public int id { get; set; }
public string name { get; set; }
public string username { get; set; }
public string user_role { get; set; }
public string CreatedUserName { get; set; }
public string ModifiedUserName { get; set; }
...
}
You can put all data that you need in your view model... Then, I'll recommend you create a class in your model with all the queries that you need (you have to investigate how to do), but you can get the queries from your controller (if you want).
Well, edit your controller function:
public ActionResult UserList()
{
List<userViewModel> userVM = new List<userViewModel>(); //Important! Don't return all the query, just the data that you need.
IEnumerable<user> users = null;
users = dbEntities.users.ToList();
if (users != null)
{
var userLists = (from a in users join b in users on a.created_user_id equals b.id select new { a.name, a.username, a.password, a.user_role, a.is_enable, a.is_approved, CreatedUserName = b.name, a.create_time, a.is_android, a.device_id }).ToList(); //I'm going to suppose that your query is ok and you get all the data that you need...
foreach (var item in userLists)
{
userVM.Add(new userVM(){
userVM.name = item.name;
userVM.username = item.username;
userVM.user_role = item.user_role;
.......
});
}
}
return View(userVM); //return your view model
}
Finally, modify your view and call the ViewModel userViewModel
#model Model.ViewModel.userViewModel //It depends on the namespace
//Then try something likes this...
#foreach (var aUser in Model)
{
<tr>
<td>#aUser.name</td>
<td>#aUser.username</td>
.....
<td>#Html.ActionLink("Edit", "UserEdit","Users")</td>
<td>#Html.ActionLink("Delete", "UserDelete", "Users")</td>
</tr>
}
That's the idea, improve my answer. ;)

List View displaying Roles and Application User

I am very new to MVC5 and am trying to display a List view of user details and the name of the role they are in. I have set up the roles using MVC Identity.
Below is my ViewModel
public class UserRoleViewModel
{
public string UserRoleVMId { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string Role { get; set; }
public bool AccountEnabled { get; set; }
}
And my controller:
public ActionResult Index()
{
var model = (from d in db.Users
select new UserRoleViewModel()
{
UserRoleVMId = d.Id,
Title = d.Title,
FirstName = d.FirstName,
Surname = d.Surname,
Email = d.Email,
AccountEnabled = d.AccountEnabled,
Role = d.Role.Name
}).ToList();
return View(model);
}
I am unsure of how to display the role name and am getting the error message
does not contain the definition for Role and no extension method Role
at
Role = d.Role.Name
Any help would be greatly appreciated
Thanks
You can use the GetRoles method :
viewModel.RolesForThisUser = UserManager.GetRoles(userId).ToList();
This is useful when you want to display the details of one user.
You should use The property Roles, since a user could have multiple roles:
public ActionResult Index()
{
using(var context = new IdentityDbContext()){
viewModel =
context.Users
.Include("Roles")
.Select(u =>
new UserRoleViewModel {
UserRoleVMId = u.Id,
Title = u.Title,
FirstName = u.FirstName,
Surname = u.Surname,
Email = u.Email,
AccountEnabled = u.AccountEnabled,
Role = u.Roles.First().Role.ToString()
}
).ToList();
}
return View(viewModel);
}
You can use this code
return view(db.Users.ToList());
and in your view on top use
#model IEnumerable<YourProjectName.Models.Users>
and use this foreach for show content
#foreach(var item in Model){<p>#item.Title</p> and etc }

Why am i being told my model property (id) doesn't exist, when it does?

I have a model property RegimeItemID which I am using in my controller. However, when I am trying to call it, it is giving an error that it doesnt exist. What have I done wrong?
Controller
public ActionResult ExerciseIndex(int? id, UserExerciseViewModel vmodel)
{
User user = db.Users.Find(id);
//user.RegimeItems = ChosenExercises();
UserExerciseViewModel model = new UserExerciseViewModel { AvailableExercises = GetAllExercises(), RequestedExercises = ChosenExercises(user, vmodel) };
//user.RegimeItems = db.RegimeItems.Find(model.SelectedExercise);
user.RegimeItems = model.RequestedExercises;
return View(model);
}
private List<RegimeItem> ChosenExercises(User user, UserExerciseViewModel model)//RegimeItem regimeItem)//User user, RegimeItem regimeItem)
{
return db.Users.Where(r => r.RegimeItems.RegimeItemID == user.UserID).ToList();
}
Models
public class User
{
public int UserID { get; set; }
public ICollection<RegimeItem> RegimeItems { get; set; }
public User()
{
this.RegimeItems = new List<RegimeItem>();
}
}
public class RegimeItem
{
public int RegimeItemID { get; set; }
public Exercise RegimeExercise { get; set; }
}
ViewModel
public class UserExerciseViewModel
{
public List<Exercise> AvailableExercises { get; set; }
public List<RegimeItem> RequestedExercises { get; set; }
public int? SelectedExercise { get; set; }
public int[] AvailableSelected { get; set; }
public int[] RequestedSelected { get; set; }
public string SavedRequested { get; set; }
}
I am getting the error:
'System.Collections.Generic.ICollection<Project.Model.RegimeItem>' does not contain a definition for 'RegimeItemID' and no extension method 'RegimeItemID' accepting a first argument of type 'System.Collections.Generic.ICollection<Project.Model.RegimeItem>' could be found (are you missing a using directive or an assembly reference?)
I am getting it on this line in the controller:
return db.Users.Where(r => r.RegimeItems.RegimeItemID == user.UserID).ToList();
RegimeItems is a collection of RegimeItem elements - it's not a single element, so it doesn't have a single ID.
It sounds like you may want something like:
return db.Users
.Where(r => r.RegimeItems.Any(ri => ri.RegimeItemID == user.UserID))
.ToList();
That will find users who have any regime items with a regime item ID equal to their user ID. It's not really clear whether that's a sensible query though - why would a regime item ID be equal to a user ID? It also doesn't have the right return type - that's going to return a list of Users, not RegimeItems.
It seems more likely that you want something like:
return db.Users
.Where(u => u.UserID == user.UserID)
.SelectMany(u => u.RegimeItems)
.ToList();
Or possibly:
return db.Users
.Where(u => u.UserID == user.UserID)
.Single()
.RegimeItems
.ToList();
(You should look at the queries involved in each case.)

Categories