J'ai le modèle de projet par défaut du site Web ASP.NET MVC 5 et j'essaie de répertorier tous les utilisateurs avec des noms de rôle (et non des ID).
La requête est:
db.Users.Include(u => u.Roles).ToList()
Ensuite, je veux imprimer les noms de rôle avec quelque chose comme:
@string.Join(", ", user.Roles.Select(r => r.RoleId))
Le problème est que je ne peux atteindre que RoleId
, pas la classe de rôle où Name
et d'autres propriétés sont stockées.
Je pourrais exécuter une autre sélection pour obtenir tous les rôles, puis l’utiliser comme recherche. Ou écrivez une jointure dans la requête directement? Je ne sais pas comment, car je n'ai pas accès à la table avec les entités IdentityUserRole
qui lient des utilisateurs et des rôles.
La racine du problème semble être le fait que la collection de rôles de IdentityUserRole
(non Role
) ne contient que RoleId
et UserId
.
public class IdentityUserRole<TKey> {
public virtual TKey RoleId { get; set; }
public virtual TKey UserId { get; set; }
}
Je pensais que si l'on voulait faire une relation N-à-N dans EF, il devrait mettre directement la collection de Roles
et ensuite écraser OnModelCreating
et spécifier les relations. Cette approche semble compliquer la navigation dans les objets.
Pourquoi ont-ils décidé d'inclure IdentityUserRole
en tant qu'entité supplémentaire? Pour pouvoir ajouter des données supplémentaires aux relations? Au prix de ne pas pouvoir naviguer des utilisateurs aux rôles?
La façon dont je le fais est:
using (var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationContext()))
{
var rolesForUser = await userManager.GetRolesAsync(userId);
// rolesForUser now has a list role classes.
}
L'équipe d'identité a créé deux gestionnaires: RoleManager
pour trier les rôles (pas les rôles d'utilisateur cependant) et UserManager
essentiellement pour tout ce qui concerne l'authentification. Il y a aussi une SignInManager
aussi mais pas nécessaire.
Donc, UserManager
trouve des utilisateurs, crée des utilisateurs, supprime des utilisateurs, envoie des courriels, etc. la liste s'allonge.
Donc, ma Action
pourrait ressembler à ceci:
public async Task<ActionResult> GetRolesForUser(string userId)
{
using (
var userManager =
new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
var rolesForUser = await userManager.GetRolesAsync(userId);
return this.View(rolesForUser);
}
}
Pour exécuter du SQL brut, vous pouvez faire quelque chose comme ceci:
Créez la classe à laquelle Entity Framework peut mapper, en fonction du résultat de votre requête:
public class UserWithRole
{
public string UserName {get;set;} // You can alias the SQL output to give these better names
public string Name {get;set;}
}
using (var context = new DbContext())
{
var sql = @"
SELECT AspNetUsers.UserName, AspNetRoles.Name
FROM AspNetUsers
LEFT JOIN AspNetUserRoles ON AspNetUserRoles.UserId = AspNetUsers.Id
LEFT JOIN AspNetRoles ON AspNetRoles.Id = AspNetUserRoles.RoleId
WHERE AspNetUsers.Id = @Id";
var idParam = new SqlParameter("Id", theUserId);
var result = context.Database.ExecuteQuery<UserWithRole>(sql, idParam);
}
Assez simple!
Si vous aliasez vos colonnes de retour SQL:
SELECT AspNetUSers.UserName, AspNetRoles.Name As RoleName
Ensuite, votre classe DTO peut ressembler à ceci:
public class UserWithRole
{
public string UserName {get;set;}
public string RoleName {get;set;}
}
Ce qui est évidemment beaucoup plus propre.
Voici comment je le fais avec MVC 5, Identity 2.0 et un utilisateur et un rôle personnalisés décrits par John Atten
Dans le contrôleur
public virtual ActionResult ListUser()
{
var users = UserManager.Users;
var roles = new List<string>();
foreach (var user in users)
{
string str = "";
foreach (var role in UserManager.GetRoles(user.Id))
{
str = (str == "") ? role.ToString() : str + " - " + role.ToString();
}
roles.Add(str);
}
var model = new ListUserViewModel() {
users = users.ToList(),
roles = roles.ToList()
};
return View(model);
}
Dans ViewModel
public class ListUserViewModel
{
public IList<YourAppNamespace.Models.ApplicationUser> users { get; set; }
public IList<string> roles { get; set; }
}
Et dans ma vue
@{
int i = 0;
}
@foreach (var item in Model.users)
{
@Html.DisplayFor(modelItem => item.Name)
[... Use all the properties and formating you want ... and ]
@Model.roles[i]
i++;
}
Je pense que sa mauvaise technique pour exécuter du SQL dynamique à partir de votre application C #. Voici ma méthode:
Modèle:
public class ApplicationRole : IdentityRole
{
public ApplicationRole() : base() { }
public ApplicationRole(string name) : base(name) { }
public string Description { get; set; }
}
Manette:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Collections.Generic;
//Example for Details.
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var role = await RoleManager.FindByIdAsync(id);
// Get the list of Users in this Role
var users = new List<ApplicationUser>();
// Get the list of Users in this Role
foreach (var user in UserManager.Users.ToList())
{
if (await UserManager.IsInRoleAsync(user.Id, role.Name))
{
users.Add(user);
}
}
ViewBag.Users = users;
ViewBag.UserCount = users.Count();
return View(role);
Afficher (à l'aide de ApplicationRole)
<div>
<h4>Roles.</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd>
@Html.DisplayFor(model => model.Name)
</dd>
</dl>
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Description)
</dt>
<dd>
@Html.DisplayFor(model => model.Description)
</dd>
</dl>
</div>
<h4>List of users in this role</h4>
@if (ViewBag.UserCount == 0)
{
<hr />
<p>No users found in this role.</p>
}
<table class="table">
@foreach (var item in ViewBag.Users)
{
<tr>
<td>
@item.UserName
</td>
</tr>
}
</table>
Merci à Callum Linington pour sa réponse. Essayez juste de clarifier un peu les choses pour les débutants comme moi ... Voici les étapes à suivre pour obtenir une liste des utilisateurs avec leurs rôles.
1- Créez un modèle de vue appelé "UsersWithRoles" avec les propriétés indiquées ci-dessous:
2- Créez un contrôleur appelé "RolesController", puis ajoutez-y le morceau de code suivant.
public ActionResult Index()
{
using (var context = new ApplicationDbContext())
{
var sql = @"
SELECT AspNetUsers.UserName, AspNetRoles.Name As Role
FROM AspNetUsers
LEFT JOIN AspNetUserRoles ON AspNetUserRoles.UserId = AspNetUsers.Id
LEFT JOIN AspNetRoles ON AspNetRoles.Id = AspNetUserRoles.RoleId";
//WHERE AspNetUsers.Id = @Id";
//var idParam = new SqlParameter("Id", theUserId);
var result = context.Database.SqlQuery<UserWithRoles>(sql).ToList();
return View(result);
}
}
et voici à quoi devrait ressembler le RolesController:
3- Ajoutez la page d'index au dossier Roles et ajoutez le code suivant.
@model IEnumerable<MVC_Auth.ViewModels.UserWithRoles>
<div class="row">
<h4>Users</h4>
<table class="table table-hover table-responsive table-striped table-bordered">
<th>User Name</th>
<th>Role</th>
@foreach (var user in Model)
{
<tr>
<td>@user.UserName</td>
<td>@user.Role</td>
</tr>
}
</table>
</div>
Voici le résultat
Merci.
using System.Linq;
using System.Data;
using System.Data.Entity;
var db = new ApplicationDbContext();
var Users = db.Users.Include(u => u.Roles);
foreach (var item in Users)
{
string UserName = item.UserName;
string Roles = string.Join(",", item.Roles.Select(r=>r.RoleId).ToList());
}