.net core 2.1 identity获取所有用户及其关联角色

nnsrf1az  于 2021-06-21  发布在  Mysql
关注(0)|答案(11)|浏览(450)

我正在尝试为一个用户管理管理页面提取所有标识用户及其关联角色。我认为这相当容易,但显然不是。我尝试了以下解决方案:https://stackoverflow.com/a/43562544/5392786 但到目前为止还没有成功。
到目前为止,我掌握的情况如下:
应用程序用户:

public class ApplicationUser : IdentityUser
{
    public List<IdentityUserRole<string>> Roles { get; set; }
}

数据库上下文

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

启动标识码

services.AddIdentity<ApplicationUser, IdentityRole>(options => options.Stores.MaxLengthForKeys = 128)
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

我要在其中显示列表的页面:

public class IndexModel : PageModel
{
    private readonly UserManager<ApplicationUser> userManager;

    public IndexModel(UserManager<ApplicationUser> userManager)
    {
        this.userManager = userManager;
    }

    public IEnumerable<ApplicationUser> Users { get; set; }

    public void OnGetAsync()
    {
        this.Users = userManager.Users.Include(u => u.Roles).ToList();
    }
}

我打电话时出现以下错误 userManager.Users.Include(u => u.Roles).ToList(); :
mysql.data.mysqlclient.mysqlexception:“字段列表”中的“未知列”“u.roles.applicationserid”

sq1bmfud

sq1bmfud1#

我实现了这个问题的解决方案,在性能和复杂性之间提供了一个我满意的平衡。我们执行一些数据库往返,每个角色一次,而不是每个用户一次。不需要dbmigrations或类重写。

//Fetch all the Users
        var users = await userManager.Users
            .Select(u => new { User = u, Roles = new List<string>() })
            .ToListAsync();

        //Fetch all the Roles
        var roleNames = await roleManager.Roles.Select(r => r.Name).ToListAsync();

        foreach (var roleName in roleNames)
        {
            //For each role, fetch the users
            var usersInRole = await userManager.GetUsersInRoleAsync(roleName);

            //Populate the roles for each user in memory
            var toUpdate = users.Where(u => usersInRole.Any(ur => ur.Id == u.User.Id));
            foreach (var user in toUpdate)
            {
                user.Roles.Add(roleName);
            }
        }
tzdcorbm

tzdcorbm2#

对于dotnetcore3.1,我使用了以下一般方法。

// _appContext is an instance of IdentityDbContext<ApplicationUser>

_appContext.Users
.SelectMany(
    // -- below emulates a left outer join, as it returns DefaultIfEmpty in the collectionSelector
    user => _appContext.UserRoles.Where(userRoleMapEntry => user.Id == userRoleMapEntry.UserId).DefaultIfEmpty(),
    (user, roleMapEntry) => new { User = user, RoleMapEntry = roleMapEntry })
.SelectMany(
    // perform the same operation to convert role IDs from the role map entry to roles
    x => _appContext.Roles.Where(role => role.Id == x.RoleMapEntry.RoleId).DefaultIfEmpty(),
    (x, role) => new {User = x.User, Role = role})
.ToList() // runs the queries and sends us back into EF Core LINQ world
.Aggregate(
    new Dictionary<ApplicationUser, List<IdentityRole>>(), // seed
    (dict, data) => {
        // safely ensure the user entry is configured
        dict.TryAdd(data.User, new List<IdentityRole>());
        if (null != data.Role)
        {
            dict[data.User].Add(data.Role);
        }
        return dict;
    },
    x => x);

它生成的sql非常简单合理:

SELECT "a"."Id", 
"a"."AccessFailedCount", 
"a"."ConcurrencyStamp", 
"a"."Email", 
"a"."EmailConfirmed", 
"a"."LockoutEnabled", 
"a"."LockoutEnd", 
"a"."NormalizedEmail", 
"a"."NormalizedUserName", 
"a"."PasswordHash", 
"a"."PhoneNumber", 
"a"."PhoneNumberConfirmed", 
"a"."SecurityStamp", 
"a"."TwoFactorEnabled", 
"a"."UserName", 
"a1"."Id", 
"a1"."ConcurrencyStamp", 
"a1"."Name", 
"a1"."NormalizedName"
FROM "AspNetUsers" AS "a"
LEFT JOIN "AspNetUserRoles" AS "a0" ON "a"."Id" = "a0"."UserId"
LEFT JOIN "AspNetRoles" AS "a1" ON "a0"."RoleId" = "a1"."Id"
f1tvaqid

f1tvaqid3#

接受的答案要求通过扩展自定义标识,否则将禁用rolemanager和usermanager。自定义asp.net核心标识时,不应再使用addentityframeworkstores。因为它将覆盖您以前的所有设置和自定义默认标识服务。首先,您需要创建具有以下签名的新服务:为什么这会违反类型参数“tuser”的约束?
不扩展,使用usermanager和rolemanager:

namespace identityDemo.Controllers
{
    public class UserManagementController : Controller
    {
        private readonly ApplicationDbContext _context;
        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly UserManager<IdentityUser> _userManager;

            public UserManagementController(ApplicationDbContext context, 
UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager)
        {
            _context = context;
            _roleManager = roleManager; 
            _userManager = userManager; 
        }

        // GET: ApplicationUserRoles
        public async Task<IActionResult> GetApplicationUsersAndRoles()
        {
            return View(new UserMv(
                (from user in await _userManager.Users.ToListAsync()
                 select new UserMv(user, GetUserRoles(user).Result)).ToList()));
        }

        private async Task<List<string>> GetUserRoles(IdentityUser user)
        {
            return new List<string>(await _userManager.GetRolesAsync(user));
        }
}

使用简单的构造函数Map到dto:

namespace IdentityDemo.Models.ModelView
{
    public class UserMv
    {
public UserMv(IdentityUser aus, List<string> userRoles)
        {
            UserId = aus.Id;
            UserName = aus.UserName;
            RolesHeld = userRoles; 
            Email = aus.Email;
            EmailConfirmed = aus.EmailConfirmed;
            LockoutEnabled = aus.LockoutEnabled;
            AccessFailedCount = aus.AccessFailedCount;
        }
}

和startup.cs

services.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
jk9hmnmh

jk9hmnmh4#

我通过创建一个包含我需要的所有列(包括角色)的视图并将其添加到上下文中来解决这个问题。

rekjcdws

rekjcdws5#

通过调用\u usermanager.getrolesasync(user)函数循环用户列表并获取用户角色,并在一个字符串变量中使用“,”循环用户角色和拆分角色

[HttpPost]
    public async Task<IActionResult> OnPostGetPagination()
    {

        var users = await _userManager.Users.ToListAsync();
        InputModel inputModel = new InputModel();
        foreach (var v in users)
        {
            inputModel = new InputModel();
            var roles = await _userManager.GetRolesAsync(v);
            inputModel.Email = v.UserName;
            inputModel.role = "";
            foreach (var r in roles)
            {
                if (!inputModel.role.Contains(","))
                {
                    inputModel.role = r;
                }
                else
                {
                    inputModel.role = "," + r;
                }
            }
            Input2.Add(inputModel);
        }

    }

祝你好运

plicqrtu

plicqrtu6#

我需要在视图中显示一个用户拥有的所有角色,而不是这里已经提供的解决方案,我使用了这个快速而肮脏的东西:

@foreach(var user in Model.Users)
        {
        <tr>
            <td>@user.Email</td>
            <td>@String.Join(", ", @Model._userManager.GetRolesAsync(user).GetAwaiter().GetResult().ToArray())</td>
        </tr>
        }

_usermanager必须是公共的才能工作。而user只是identityuser的一个示例。

lrl1mhuk

lrl1mhuk7#

效果很好。我使用的是整数键,所以我用int替换了string

ApplicationRole : IdentityRole<int>
ApplicationUserRole : IdentityUserRole<int>
ApplicationUser : IdentityUser<int>

ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, 
IdentityUserClaim<int>,
ApplicationUserRole, IdentityUserLogin<int>, IdentityRoleClaim<int>, 
IdentityUserToken<int>>

linq:roleid=(从m.userroles中的a选择a.role.id).firstordefault(),

fzsnzjdm

fzsnzjdm8#

参考意见
首先是获取数据的代码

public async Task<IEnumerable<AccountViewModel>> GetUserList()
        {
            var userList = await (from user in _context.Users
                                  select new
                                  {
                                      UserId = user.Id,
                                      Username = user.UserName,
                                      user.Email,
                                      user.EmailConfirmed,
                                      RoleNames = (from userRole in user.Roles //[AspNetUserRoles]
                                                   join role in _context.Roles //[AspNetRoles]//
                                                   on userRole.RoleId
                                                   equals role.Id
                                                   select role.Name).ToList()
                                  }).ToListAsync();

            var userListVm = userList.Select(p => new AccountViewModel
            {
                UserId = p.UserId,
                UserName = p.Username,
                Email = p.Email,
                Roles = string.Join(",", p.RoleNames),
                EmailConfirmed = p.EmailConfirmed.ToString()
            });

            return userListVm;
        }

在asp.net core 2.1中,我们需要像这样设置applicationrole,以便获得用户的角色。您需要定义要显式公开给用户使用的数据

public class ApplicationRole : IdentityRole
    {
        public virtual ICollection<IdentityUserRole<string>> Users { get; set; }

        public virtual ICollection<IdentityRoleClaim<string>> Claims { get; set; }
    }

最后

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
            {
                relationship.DeleteBehavior = DeleteBehavior.Restrict;
            }

            modelBuilder.Entity<User>().HasMany(u => u.Claims).WithOne().HasForeignKey(c => c.UserId).IsRequired().OnDelete(DeleteBehavior.Cascade);
            modelBuilder.Entity<User>().HasMany(u => u.Roles).WithOne().HasForeignKey(r => r.UserId).IsRequired().OnDelete(DeleteBehavior.Cascade);

            modelBuilder.Entity<ApplicationRole>().HasMany(r => r.Claims).WithOne().HasForeignKey(c => c.RoleId).IsRequired().OnDelete(DeleteBehavior.Cascade);
            modelBuilder.Entity<ApplicationRole>().HasMany(r => r.Users).WithOne().HasForeignKey(r => r.RoleId).IsRequired().OnDelete(DeleteBehavior.Cascade);

            modelBuilder.EnableAutoHistory(null);
        }

结果将是用户名和用户角色。如果用户有超过1个角色的数据将显示如下管理员,编辑等。。。
完整的代码可以在这里找到,希望对你有所帮助。

lvjbypge

lvjbypge9#

我现在已经实现了以下解决方案。
正如codenotfound在评论中指出的,identityuser过去有一个 Roles 财产。在.NETCore中不再是这种情况。github上的这个评论/问题似乎是.netcore当前的解决方案。我尝试用以下代码实现它:
应用程序用户

public class ApplicationUser : IdentityUser
{
    public ICollection<ApplicationUserRole> UserRoles { get; set; }
}

应用程序角色

public class ApplicationUserRole : IdentityUserRole<string>
{
    public virtual ApplicationUser User { get; set; }
    public virtual ApplicationRole Role { get; set; }
}

应用程序角色

public class ApplicationRole : IdentityRole
{
    public ICollection<ApplicationUserRole> UserRoles { get; set; }
}

数据库上下文

public class ApplicationDbContext
    : IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserClaim<string>,
    ApplicationUserRole, IdentityUserLogin<string>,
    IdentityRoleClaim<string>, IdentityUserToken<string>>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<ApplicationUserRole>(userRole =>
        {
            userRole.HasKey(ur => new { ur.UserId, ur.RoleId });

            userRole.HasOne(ur => ur.Role)
                .WithMany(r => r.UserRoles)
                .HasForeignKey(ur => ur.RoleId)
                .IsRequired();

            userRole.HasOne(ur => ur.User)
                .WithMany(r => r.UserRoles)
                .HasForeignKey(ur => ur.UserId)
                .IsRequired();
        });
    }
}

启动

services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.Stores.MaxLengthForKeys = 128)
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

最后,确保在使用它时,您急切地加载用户的userrole,然后加载userrole的role,如下所示:

this.Users = userManager.Users.Include(u => u.UserRoles).ThenInclude(ur => ur.Role).ToList();

我有个问题 Role 各自的财产 UserRole 为null,通过添加 .ThenInclude(ur => ur.Role) 部分。
microsoft文档的多级加载:https://docs.microsoft.com/en-us/ef/core/querying/related-data#including-多层次
asp core 2.2更新
固有的 IdentityUserRole<Guid> 您可能还需要删除modelbuilder中的代码才能使迁移工作正常。

xwmevbvl

xwmevbvl10#

因为这是谷歌搜索的最热门结果;现在,您只需加入userroles dbset(如果您的db上下文继承自identitydbcontext)。
e、 g outer将roles表连接到任何用户角色,然后创建我们的manageusermodel(api的applicationuser类的简化信息):

var employees = (from bb in _appContext.Users
            join roleIds in _appContext.UserRoles on bb.Id equals roleIds.UserId
            join role in _appContext.Roles on roleIds.RoleId equals role.Id into roles
            orderby bb.LastName, bb.FirstName
            where roles !=null && roles.Any(e => e.Name == Permissions.RoleNames.Administrator || e.Name == Permissions.RoleNames.Employee)
            select ManageUserModel.FromInfo(bb, roles)).ToList();

public static ManageUserModel FromInfo(ApplicationUser info, IEnumerable<UserRole> roles)
    {
        var ret= FromInfo(info);
        ret.Roles = roles.Select(e => new SimpleEntityString() {Id=e.Id, Text=e.Name}).ToList();
        return ret;
    }

这还演示了使用任何角色信息的where子句(上面只选择管理员和雇员角色中的用户)。
注意:这个内部连接identityuserrole,因此只有具有角色的用户才会返回,如果您希望所有用户只需在连接roleids的末尾添加一个“into identintroles”。。。行并相应地修改其余条件。

2jcobegt

2jcobegt11#

您可以使用efcore5.0多对多特性,避免子类identityuserrole/identityrole。
应用程序用户

using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;

public class ApplicationUser : IdentityUser
{
    public ICollection<IdentityRole> Roles { get; set; }
}

数据库上下文:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    ...

    protected override void OnModelCreating(ModelBuilder builder)
    {
            base.OnModelCreating(builder);

            builder.Entity<ApplicationUser>()
                .HasMany(u => u.Roles)
                .WithMany("Users")
                .UsingEntity<IdentityUserRole<string>>(
                    userRole => userRole.HasOne<IdentityRole>()
                        .WithMany()
                        .HasForeignKey(ur => ur.RoleId)
                        .IsRequired(),
                    userRole => userRole.HasOne<ApplicationUser>()
                        .WithMany()
                        .HasForeignKey(ur => ur.UserId)
                        .IsRequired());
    }
}

相关问题