web-dev-qa-db-fra.com

Comment répertorier les utilisateurs avec des noms de rôle dans ASP.NET MVC 5

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?

11
NightElfik

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.

14
Callum Linington

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++;
}
3
remi

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>
0
Danny Rancher

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:

 enter image description here

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:

 enter image description here

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 

 enter image description here

Merci.

0
user2662006
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());
            }
0
Hossein Hajizadeh