I'm trying to display all the available Roles from my RoleManager
in a DropDownList
and then select the specific Role that the user is member of. The requirements for this are that the user only be in one Role (“User, Admin, or NoAccess”).
When I get a list of the Roles and assign them to my ViewModel I get an error when I try to display them I get an error:
Cannot convert from ‘System.Collections.Generic.IEnumerable<SiteName.Models.AppRole>’ to ‘Systems.Collections.IEnumerable<System.Web.Mvc.SelectListItem>.
Do I need to create a ViewModel just for getting the RoleName and RoleId, then loop through the list of Roles I get from the RoleManger and populate a List of that ViewModel to pass to my view? That seems like extra code when I already have a class (AppRole) that inherits from IdentityRole which should have the Role.Name and Role.Id.
Here is my ViewModel for editing/displaying a user:
public class UserEditVM : BaseViewModel
{
public string Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string UserRole { get; set; }
public string RoleID { get; set; }
public IEnumerable<AppRole> AllRoles { get; set; }
public IEnumerable<SelectListItem> RolesList { get; set; }
public bool IsActive { get; set; }
}
This is from my Controller where I am creating a VM object. I’ve tried the list as an AppRole and as a SelectListItem. Both are giving me the same error as above.
private async Task<UserEditVM> createUserVM(AppUser user)
{
var userRoles = await UserManager.GetRolesAsync(user.Id);
var allRoles = RoleManager.Roles.ToList();
string roleName = userRoles.Count() == 0 ? "User" : userRoles[0];
UserEditVM editUser = new UserEditVM();
editUser.Id = user.Id;
editUser.UserName = user.UserName;
editUser.FirstName = user.FirstName;
editUser.LastName = user.LastName;
editUser.Email = user.Email;
editUser.UserRole = roleName;
editUser.AllRoles = RoleManager.Roles.ToList();
editUser.RolesList = new SelectList(RoleManager.Roles.ToList(), "Id", "Name"); ;
editUser.IsActive = user.IsActive;
return editUser;
}
This is the AppRole. Inheriting from the IdentityRole it should already have the Role .Name and .Id available.
public class AppRole : IdentityRole
{
public AppRole() : base() { }
public AppRole(string name) : base(name) { }
}
This is my View where I’m trying to display them in a DDL.
<div class="form-group">
<label>Role:</label> @Html.DisplayNameFor(model => model.UserRole)
@Html.DropDownListFor(model => model.Id, Model.AllRoles)
</div>
UPDATE
Here is my updated VM
public class UserEditVM : BaseViewModel
{
public string Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string UserRole { get; set; }
public string RoleID { get; set; }
public IEnumerable<AppRole> AllRoles { get; set; }
public IEnumerable<SelectListItem> RolesList { get; set; }
public string SelectedRoleId { get; set; }
public bool IsActive { get; set; }
}
This is my controller. My AppRole class is inheriting from IdentityRole which has the .Id and .Name property.
private async Task<UserEditVM> createUserVM(AppUser user)
{
var userRoles = await UserManager.GetRolesAsync(user.Id);
var allRoles = RoleManager.Roles.ToList();
string roleName = userRoles.Count() == 0 ? "User" : userRoles[0];
UserEditVM editUser = new UserEditVM();
editUser.Id = user.Id;
editUser.UserName = user.UserName;
editUser.FirstName = user.FirstName;
editUser.LastName = user.LastName;
editUser.Email = user.Email;
editUser.UserRole = roleName;
editUser.AllRoles = RoleManager.Roles.Select(r => new AppRole { Id = r.Id, Name = r.Name }).ToList();
//editUser.RolesList = new SelectList(RoleManager.Roles.ToList(), "Id", "Name"); ;
editUser.IsActive = user.IsActive;
return editUser;
}
Here is what I tried in my View
<div class="form-group">
<label>Role:</label> @Html.DisplayNameFor(model => model.UserRole)
@Html.DropDownListFor(model => model.SelectedRoleId, Model.AllRoles, "Select")
</div>
In the View I’m still getting an error “cannot convert from ‘System.Collections.Generic.IEnumerable<SiteName.Models.AppRole>’ to ‘System.Collections.Generic.IEnumerable<System.Web.Mvc.SelectListItem>’
Fixed Update
Here is the final fix. Ultimatley the issue was with the Using statement in my ViewModel. When I first added the IEnumerable I used the QuickFix feature in VisualStudio 2015 to resolve the reference. The problem with that was that it suggested "using System.Web.WebPages.Html" when what I really needed was "using System.Web.Mvc". That could be seen in the last error I was getting. Once I switched the using statements it worked.
This is what I ended up with
using System.Collections.Generic;
//using System.Web.WebPages.Html;
using System.Web.Mvc;
namespace SampleMvcSite.Models
{
public class UserEditVM : BaseViewModel
{
public string Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string UserRole { get; set; }
public string RoleID { get; set; }
public IEnumerable<AppRole> AllRoles { get; set; }
public IEnumerable<SelectListItem> RolesList { get; set; }
public string SelectedRoleId { get; set; }
public bool IsActive { get; set; }
}
}
Controller
private async Task<UserEditVM> createUserVM(AppUser user)
{
var userRoles = await UserManager.GetRolesAsync(user.Id);
string roleName = userRoles.Count() == 0 ? "User" : userRoles[0];
var allRoles = RoleManager.Roles.ToList();
UserEditVM editUser = new UserEditVM();
editUser.Id = user.Id;
editUser.UserName = user.UserName;
editUser.FirstName = user.FirstName;
editUser.LastName = user.LastName;
editUser.Email = user.Email;
editUser.UserRole = roleName;
editUser.RolesList = allRoles.Select(s => new SelectListItem
{
Value = s.Id.ToString(),
Text = s.Name,
Selected = s.Name == roleName ? true : false
});
editUser.IsActive = user.IsActive;
return editUser;
}
View
<div class="form-group">
<label>Current Role:</label>
@Html.DropDownListFor(model => model.SelectedRoleId, Model.RolesList, "Select Role")
</div>
For your compilation error.
Looks like RoleManager.Roles
return you a collection of a different type than what you declared in the view model (IEnumerable<AppRole>
). In that case, you need to convert it to the a collection of AppRole.
editUser.AllRoles = RoleManager.Roles
.Select(s=>new AppRole { Id = s.Id, Name = s.Name).toList();
Assuming the AppRole class has Id and Name properties which you want to use for displaying the dropdown. If your property names are different, Changes the Projection part of the above code.
But apparently, you are not using the AllRoles
collection in your view.
To fix your Razor error
Add one more property to your viewmodel to store the selected Role from the dropdown
public class UserEditVM : BaseViewModel
{
public int SelectedRoleId {set;get;}
public IEnumerable<SelectListItem> RolesList { get; set; }
//Other properties as needed.
}
And in your GET action method, You get all the roles ( Which could be a collection of Role class or some custom class), convert that to a collection of SelectListItem
and assign that to the RolesList
property of the UserEditVM
object.
public ActionResult Create()
{
var vm=new UserEditVM();
var allRoles= RoleManager.Roles.ToList();
vm.RolesList = allRoles.Select(s => new SelectListItem
{
Value = s.Id.ToString(),
Text = s.Name
});
return View(vm);
}
And in the razor view, use the RolesList
property which is a collection of SelectListItem
@model YourNameSpace.UserEditVM
@Html.DropDownListFor(x=> x.SelectedRoleId, Model.RolesList,"Select")
Now when user submits the form, The selected role id will be in the SelectedRoleId
property of the posted model.
[HttpPost]
public ActionResult Edit(UserEditVM model)
{
//check model.SelectedRoleId
//to do : Save and Redirect
}