Get Users where are not in role admin and simpleuser - c#

I am using .Net Core 2.1 and I want to exclude the users that have the role "Admin" and "SimpleUser" I had followed this example Link
How i can add the where part?
So far i have tried this with no luck:
ViewData["ApplicationUserId"] = new SelectList( _context.Users.Include(u=>u.UserRoles).ThenInclude(u=>u.Role).Where(o=>o.UserRoles!="Admin"), "Id", "Company", products.ApplicationUserId);
ApplicationUserRole Class:
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}

The message in the attached image suggests that u.UserRoles is not a string, try something like u.UserRoles.All(r=>r.RoleId!="Admin" && r.RoleId!="SimpleUser") in your Where

You will need to check that none of the roles are of "Admin" or "SimpleUser"
ViewData["ApplicationUserId"] = new SelectList( _context.Users.Include(u=>u.UserRoles).ThenInclude(u=>u.Role).Where(o => !o.UserRoles.Any(r => (r.Role == "Admin") || (r.Role == "SimpleUser"))), "Id", "Company", products.ApplicationUserId);

Related

Querying EF Core identity roles

I'm using .NET Core 3.1 and I want to query the users in my database.
So I modified my ApplicationUser class as follows:
public class ApplicationUser : IdentityUser
{
public ICollection<IdentityUserRole<string>> UserRoles { get; set; }
}
And now I'm trying to query the Users.
var users = from u in DbContext.Users
select new UserViewModel
{
Email = u.Email,
EmailConfirmed = u.EmailConfirmed,
Phone = u.PhoneNumber,
Roles = u.UserRoles.Select(r => r.Role.Name) // Whoops! Won't work
};
The problem is that UserRoles have a RoleId property, but they don't have a Role property.
So how would I get the name of those roles?
Update:
Based on Jawad's comments, I changed my code as follows:
public class ApplicationUser : IdentityUser
{
public ICollection<IdentityRole> Roles { get; set; }
}
And
Users = (from u in DbContext.Users
select new UserViewModel
{
Email = u.Email,
EmailConfirmed = u.EmailConfirmed,
Phone = u.PhoneNumber,
Roles = u.Roles.Select(r => r.Name)
})
.ToList();
I was thinking I couldn't do that because it's a many-to-many relationship. But in fact, it does compile. However, when I run it I get an error.
Invalid column name 'ApplicationUserId'.
I'm not sure where that column name is being referenced. It doesn't appear to be a column name in the database.
Update 2:
Turns out, the error above is because a database migration was pending. However, I didn't want to change the database. As I initially suspected, I can't do ApplicationUser.ICollection<IdentityRole> because there is an intermediate table.
So I got this to run, but it returned no results (because there are no foreign keys from IdentityRole to ApplicationUser.
So, the question stands: how can I query users along with their roles?
So, I don't know why IdentityUserRole doesn't include an IdentityRole property. This seems to be an unnecessary limitation.
In the end, I just changed my query.
var users = await (from u in DbContext.Users
join ur in DbContext.UserRoles on u.Id equals ur.UserId
join r in DbContext.Roles on ur.RoleId equals r.Id
select new
{
u.Email,
u.EmailConfirmed,
u.PhoneNumber,
Role = r.Name
})
.ToListAsync();
Users = (from u in users
group u.Role by u into g
select new UserViewModel
{
Email = g.Key.Email,
EmailConfirmed = g.Key.EmailConfirmed,
Phone = g.Key.PhoneNumber,
Roles = string.Join(", ", g)
})
.ToList();
I couldn't get the group by to work within the query so I'm group it after the data has been retrieved.
Anyway, it works.
You should create a new class called something like ApplicationUserRole that inherits from IdentityUserRole<string> then you just need to define the relationships between ApplicationUser -> ApplicationUserRole and ApplicationRole -> ApplicationUserRole.
modelBuilder.Entity<ApplicationUserRole>(entity =>
{
entity
.HasOne(x => x.Role)
.WithMany(x => x.UserRoles)
.HasForeignKey(x => x.RoleId);
entity
.HasOne(x => x.User)
.WithMany(x => x.UserRoles)
.HasForeignKey(x => x.UserId);
});
You should also replace any references to the Identity classes with your Application classes.
public class ApplicationUserRole : IdentityUserRole<string>
{
public ApplicationUser User { get; set; }
public ApplicationRole Role { get; set; }
}
public class ApplicationUser : IdentityUser<string>
{
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationRole : IdentityRole<string>
{
public ICollection<ApplicationUserRole> UserRoles { get; set; }
}

Get role list with associated users in ASP.NET Identity

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.

Need understanding on how to get/display User Role

I’m in the process of learning MVC with Identity 2 and I’m at the point of taking a sample project and tweaking it to fit a site that I’m trying to convert from WebForms with ASP.Net Membership provider.
What I’m having issues with at the moment is getting my head wrapped around how I get a value from another table to populate a property in a class.
I have a class called AppUser.cs and it inherits from IdentityUser. That is basically mapped to a table named “AspNetUsers” using EntityFramework. I’ve added a couple more properties (First/Last Name) to that table using EF migrations. Now I want to display what Role the user is assigned to and eventually will need to get a list of the available roles and add them to a dropdownlist. I added the RoleName property to my AppUser class but I’m assuming that since that data is not stored in the AspNetUsers table is why I’m getting an error “Invalid column name 'RoleName'”.
How would I populate the Role and/or get a list of available Roles in my AppUser class so I can display it in my View?
public class AppUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string RoleName { get; set; }
}
The model on the view “Index.cshtml” is the AppUser.
#model IEnumerable<AppUser>
This is my ActionResult for my Controller.
public ActionResult Index() {
return View(UserManager.Users);
}
private AppUserManager UserManager {
get {
return HttpContext.GetOwinContext().GetUserManager<AppUserManager>();
}
}
This is the AppUserManager.cs
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using SampleMvcSite.Models;
namespace SampleMvcSite.Infrastructure {
public class AppUserManager : UserManager<AppUser> {
public AppUserManager(IUserStore<AppUser> store)
: base(store) {
}
public static AppUserManager Create(
IdentityFactoryOptions<AppUserManager> options,
IOwinContext context) {
AppIdentityDbContext db = context.Get<AppIdentityDbContext>();
AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db));
manager.PasswordValidator = new CustomPasswordValidator {
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = true,
RequireUppercase = true
};
manager.UserValidator = new CustomUserValidator(manager) {
AllowOnlyAlphanumericUserNames = true,
RequireUniqueEmail = true
};
return manager;
}
}
}
UPDATED CODE:
I ended up creating a class called UserEditViewModel and I'm also including the AppRole class which was in the sample project I'm tweaking.
public class UserEditViewModel : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserRole { get; set; }
public IEnumerable<AppRole> AllRoles { get; set; }
}
public class AppRole : IdentityRole {
public AppRole() : base() { }
public AppRole(string name) : base(name) { }
}
Then in my controller I did the following. Keep in mind I'm only going to allow a user to be assigned to one role at a time so theoretically there should only be the one role which is why I only called on the userRoles[0]. Since I added the RoleManager after some sample users I have a couple users that aren't assigned to a Role. I added a check to make sure the Roles had a count and if not I display "User" as their role. I will go back and add code to actually add the user to the "User" role if no roles are found.
List<UserEditViewModel> UserList = new List<UserEditViewModel>();
var users = UserManager.Users;
foreach (AppUser user in users)
{
var userRoles = await UserManager.GetRolesAsync(user.Id);
string roleName = userRoles.Count() == 0 ? "User" : userRoles[0];
UserEditViewModel editUser = new UserEditViewModel();
editUser.UserName = user.UserName;
editUser.FirstName = user.FirstName;
editUser.LastName = user.LastName;
editUser.Email = user.Email;
editUser.UserRole = roleName;
editUser.AllRoles = RoleManager.Roles;
UserList.Add(editUser);
}
return View(UserList);
Here is the View
<div class="form-group">
<label>Role:</label> #Html.DisplayNameFor(model => model.UserRole)
</div>
I would look at ViewModels. It's generally bad practice to update an entity model in your view - especially security related entities. See https://www.stevefenton.co.uk/2013/03/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/
With a viewmodel, you could have a property for List<Role> that you populate in your controller and use in your view and another property like RoleId for the user's role.
In the POST you would do something like:
if (ModelState.IsValid)
{
var roleMgr = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
var userMgr = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var role = roleMgr.FindById(viewmodel.RoleId);
if (role != null) userMgr.AddToRole(viewmodel.UserId, role.Name);
}
VIEW:
<div class="form-group">
#Html.LabelFor(model => model.RoleId)
<div class="col-md-2">
#Html.DropDownListFor(model => model.RoleId, Model.RoleList, htmlAttributes: new { #class = "form-control" })
</div>
<div class="col-md-10 col-md-offset-2">
#Html.ValidationMessageFor(model => model.RoleId, "", new { #class = "text-danger" })
</div>
</div>

List of values as a user attribute

I'm using identity 2.1.0 in ASP.NET MVC 5 application, and I have pages for admin to work (create/edit/delete user) with some custom user properties I defined. However I need to store a list of values in one field. How can I achieve this by using identity, and how to show this on a web page ?
To save such values you can extend your ApplicationUser, in case of multiple values you can do this via an n:m relationship:
First create a table to store the country values in (also add to your DbContext as e.g. public DbSet<Country> Countries { get; set; }):
public class Country
{
[Key]
public int Id { get; set; } // or e.g. "string Code" to save e.g. "us"
public string Name { get; set; }
public List<ApplicationUsers> Users { get; set; }
}
then you can also add a list of Country to your ApplicationUser:
public class ApplicationUser : IdentityUser<KEY>
{
// ...
public List<Country> Countries { get; set; }
}
and finally to update the countries of a user something like the following:
var user = // get user
var countryToAdd = db.Countries.FirstOrDefault(c => c.Name == countryName) ??
new Country() { Name = countryName };
if (user.Countries == null)
user.Countries = new List<Country>() { countryToAdd };
else if (!user.Countries.Contains(countryToAdd))
user.Countries.Add(countryToAdd);
db.SaveChanges();
And to get all users from one country:
var country = db.Countries.Include(c => c.Users)
.FirstOrDefault(c => c.Name == countryName);
if (country != null)
{
var users = country.Users;
}

MVC 5 MultiSelectList Selected Values Not Working

I have a ListBox in a View displaying the possible choices. However, the user's role(s) are not shown as selected. In my UserViewModel the Roles collection contains the complete list of roles, and AspNetRoles collection contains only the roles to which the user belongs. I've tried multiple examples/variations but nothing ever shows as selected. I'm stuck.
ViewModel:
public class UserViewModel
{
public string Id { get; set; }
public string Email { get; set; }
public ICollection<AspNetRole> Roles { get; set; }
public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
}
Controller:
public ActionResult Edit(string id)
{
UserViewModel user = new UserViewModel();
AspNetUser aspNetUser = new AspNetUser();
if (!string.IsNullOrEmpty(id))
{
aspNetUser = db.AspNetUsers.Find(id);
user.Id = aspNetUser.Id;
user.Email = aspNetUser.Email;
user.AspNetRoles = aspNetUser.AspNetRoles;
var roles = (from r in db.AspNetRoles
select r).ToList();
user.Roles = roles;
}
return View(user);
}
View:
#Html.ListBox("AspNetRoles",
new MultiSelectList(Model.Roles, "Id", "Name",
Model.AspNetRoles.Select(m => m.Id)))
You have not posted your model, but it appears that property AspNetRoles is a collection of a complex object (you cannot bind to a complex object, only a value type - or in the case of ListBox, a collection of value type). You can handle this by changing AspNetRoles to int[] (assuming the ID property of Role is int)
#Html.ListBoxFor(m => m.AspNetRoles, new SelectList(Model.Roles, "Id", "Name"))
Note, Its always better to use the strongly typed helpers.

Categories