.NET Core 2.1 Identity 获取所有用户及其相关角色

.NET Core 2.1 Identity get all users with their associated roles

我正在尝试为用户管理管理页面提取我所有的身份用户及其相关角色。我认为这会相当容易,但显然不是。我尝试了以下解决方案: 但到目前为止还没有成功。

这是我目前的情况:

应用程序用户:

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

DBContext

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

启动身份代码

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

我要显示列表的 Razor 页面:

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: 'Unknown column 'u.Roles.ApplicationUserId' in 'field list''

我现在已经实施了以下解决方案。

正如 CodeNotFound 在评论中指出的那样,IdentityUser 曾经有一个 Roles 属性。在 .NET Core 中不再是这种情况。 GitHub 上的这个 comment/issue 似乎是 .Net Core 的当前解决方案。我试图用以下代码实现它:

应用程序用户

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

ApplicationUserRole

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

ApplicationRole

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

DBContext

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();

最后,确保在使用它时先加载用户的 UserRoles,然后再加载 UserRole 的角色,如下所示:

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

我遇到了一个问题,其中每个 UserRoleRole 属性 为空,通过添加 .ThenInclude(ur => ur.Role) 部分解决了这个问题。

有关多级预先加载的 Microsoft 文档:https://docs.microsoft.com/en-us/ef/core/querying/related-data#including-multiple-levels

ASP Core 2.2更新

继承自 IdentityUserRole<Guid> 不是字符串 您可能还需要删除 ModelBuilder 中的代码以使迁移工作。

引用comment

首先是获取数据的代码

 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 核心 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 个角色,数据将像这样显示 管理员、编辑等...

可在此处找到完整代码here here and here 希望对您有所帮助。

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

通过调用 _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);
        }


    }

祝你好运

因为这是排名前 google 的搜索结果;现在您可以加入 UserRoles 数据库集(如果您的数据库上下文继承自 IdentityDbContext)。

例如,将角色 table 外部加入任何用户角色,然后创建我们的 manageUserModel(为我们的 api 减少 applicationUser class 的信息):

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 子句(以上只选择了我们的 Admin 和 Employee 角色中的用户)。

注意:这个内部加入了IdentityUserRole,所以只会返回有角色的用户,如果你想要所有用户只需要在join roleIds...行的末尾添加一个"into identRoles"并修改其余条件相应。

工作完美。我使用的是整数键,所以我用 "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>>

林克: RoleId = (from a in m.UserRoles select a.Role.Id).FirstOrDefault(),

接受的答案需要通过扩展自定义身份,否则将禁用 roleManager 和 userManager。当您自定义 ASP.NET Core Identity 时,您不应再使用 AddEntityFrameworkStores。因为它将覆盖您以前的所有设置和自定义为默认身份服务。 首先,您需要使用以下签名创建新服务:

不扩展,使用 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));
        }
}

使用用于映射到 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>();

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

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

_userManager 必须是 public 才能工作。用户只是 IdentityUser 的一个实例。

对于 dotnet core 3.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"

我针对这个问题实施了一个解决方案,在性能和复杂性之间取得了我满意的平衡。我们执行一些数据库往返,每个角色一个,而不是每个用户一个。不需要 DbMigrations 或 class 覆盖。

        //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);
            }
        }

更新:此解决方案适用于 EF Core 5,但似乎从未如此,而且在 EF Core 6 中也不可能了。

您可以使用 EF Core 5.0 Many-To-Many 功能,并避免子类化 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());
    }
}

ASP.NET 核心 3.1 更新

我使用下面的代码,它运行得很好

  namespace MyProject.Pages.Roles
{
    public class DetailsModel : PageModel
    {

        public UserManager<ApplicationUser> _userManager;
        public RoleManager<IdentityRole> _roleManager;
        private readonly ApplicationDbContext _context;

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

        public IList<IdentityRole> Roles { get; set; }

        [BindProperty]
        public IList<ApplicationUser> applicationUserList { get; set; }

        [BindProperty]
        public IList<IdentityRole> allRolesList { get; set; }

        public IList<IdentityUserRole<string>> usersRoles { get; set; }
        public IList<IdentityUserRole<string>> usersRole { get; set; }
        public IList<IdentityUserRole<string>> userWithRole { get; set; }


        public Dictionary<ApplicationUser, string> itemDictionary;

        public async Task<IActionResult> OnGetAsync(string id)
        {
            if (id == null)
            {
                return NotFound();
            }


            Roles = await _context.Roles.Where(r => r.Id == id).ToListAsync();

            allRolesList = await _context.Roles.ToListAsync();

            usersRoles = await _context.UserRoles.ToListAsync();
            usersRole = await _context.UserRoles.Where(r => r.RoleId == id).ToListAsync();
            userWithRole = usersRoles.Where(u => u.RoleId == id).ToList();

            applicationUserList = await _context.Users.ToListAsync();

            itemDictionary = new Dictionary<ApplicationUser, string> { };

            foreach (var item in usersRole)
            {
                itemDictionary.Add(await _context.Users.FindAsync(id = item.UserId), item.UserId);
            }

            return Page();
        }
    }
}

绑定所有这些东西以了解发生了什么非常有用!

在详细信息 Razor 页面上,我只有

    @page "{id}"
@model MyProject.Pages.Roles.DetailsModel
@{
    Layout = "~/Views/Shared/_Layout.cshtml";
    var dict = Model.itemDictionary;
    int cou = dict.Count();
    var x = Model.applicationUserList;
}

<h5 class="bg-primary text-white text-center p-2">List of Members having the role @Model.Roles[0].Name</h5>
<table class="table">
    <thead>
        <tr>
            <th>@Html.DisplayNameFor(model => model.userWithRole[0].UserId)</th>
            <th>@Html.DisplayNameFor(model => model.userWithRole[0].RoleId)</th>
            <th>LastName, FirstName</th>
        </tr>
    </thead>

    <tbody>

        @foreach (var kvp in dict.ToArray())
        {
            <tr>
                <td>@kvp.Key</td>
                <td>@kvp.Value</td>
                <td>@kvp.Key.LastName, @kvp.Key.FirstName</td>
            </tr>
        }

    </tbody>
</table>

这里是结果:

Microsoft 文档上有一篇有用的文章https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-5.0

对我来说,公开导航属性(角色、用户)看起来像这样(NET 5):

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
    public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
    public virtual ICollection<IdentityUserToken<string>> Tokens { get; set; }
    public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}

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

public class ApplicationUserRole : IdentityUserRole<string>
{
    public virtual ApplicationUser User { get; set; }
    public virtual ApplicationRole Role { 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 modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<ApplicationUser>(b =>
        {
            // Each User can have many UserClaims
            b.HasMany(e => e.Claims)
                .WithOne()
                .HasForeignKey(uc => uc.UserId)
                .IsRequired();

            // Each User can have many UserLogins
            b.HasMany(e => e.Logins)
                .WithOne()
                .HasForeignKey(ul => ul.UserId)
                .IsRequired();

            // Each User can have many UserTokens
            b.HasMany(e => e.Tokens)
                .WithOne()
                .HasForeignKey(ut => ut.UserId)
                .IsRequired();

            // Each User can have many entries in the UserRole join table
            b.HasMany(e => e.UserRoles)
                .WithOne(e => e.User)
                .HasForeignKey(ur => ur.UserId)
                .IsRequired();
        });

        modelBuilder.Entity<ApplicationRole>(b =>
        {
            // Each Role can have many entries in the UserRole join table
            b.HasMany(e => e.UserRoles)
                .WithOne(e => e.Role)
                .HasForeignKey(ur => ur.RoleId)
                .IsRequired();
        });

    }
}

请注意,在 ApplicationDbContext 中,您可以更改主键类型(在我的例子中是字符串)

更新:

将 NuGet Duende.IdentityServer.EntityFramework.Storage 升级到 6.1.0 时出现以下错误:

CS0535 'ApplicationApiAuthorizationDbContext<TUser, TRole>' does not implement interface member 'IPersistedGrantDbContext.ServerSideSessions'

ApplicationApiAuthorizationDbContext.cs 现在需要另一个 DbSet 这样的:

    public DbSet<ServerSideSession> ServerSideSessions
    {
        get;
        set;
    }

尽管 endpoints.MapRazorPages();.

这导致了以下错误

System.Reflection.ReflectionTypeLoadException: 'Unable to load one or more of the requested types. Method 'get_ServerSideSessions' in type 'Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext`1' from assembly 'Microsoft.AspNetCore.ApiAuthorization.IdentityServer, Version=6.0.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' does not have an implementation.'

建议继续使用 Duende.IdentityServer.EntityFramework.Storage 5.2.0,直到问题得到解决。

原文:

正如@Dreamescaper 和@graycrow 所说,您可以在 EF Core 5.0 中使用阴影 many-to-many 导航,即使它不应该工作。

https://github.com/dotnet/efcore/issues/25383#issuecomment-894785144

https://github.com/dotnet/efcore/issues/23362

可能会在 EF Core 7.0 中添加支持,再次通过影子导航具有单向 many-to-many 关系,但尚未完成:

https://github.com/dotnet/efcore/issues/3864

我使用 EF Core 6.0 让它像这样工作:

应用程序用户:

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

应用程序角色:

public class ApplicationRole : IdentityRole
{
    public ICollection<ApplicationUser> Users { get; set; }

}

Program.csStartup.cs:

services.AddDefaultIdentity<ApplicationUser>(options =>
options.SignIn.RequireConfirmedAccount = false)
    .AddRoles<ApplicationRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

ApplicationApiAuthorizationDbContext:

//Based on Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext, Version=6.0.2.0
//https://github.com/dotnet/aspnetcore/issues/14161#issuecomment-533468760
public class ApplicationApiAuthorizationDbContext<TUser, TRole> : IdentityDbContext<TUser, TRole, string>, IPersistedGrantDbContext, IDisposable where TUser : IdentityUser where TRole : IdentityRole
{
    private readonly IOptions<OperationalStoreOptions> _operationalStoreOptions;

    public DbSet<PersistedGrant> PersistedGrants
    {
        get;
        set;
    }

    public DbSet<DeviceFlowCodes> DeviceFlowCodes
    {
        get;
        set;
    }

    public DbSet<Key> Keys
    {
        get;
        set;
    }

    public ApplicationApiAuthorizationDbContext(DbContextOptions options, IOptions<OperationalStoreOptions> operationalStoreOptions)
        : base(options)
    {
        _operationalStoreOptions = operationalStoreOptions;
    }

    Task<int> IPersistedGrantDbContext.SaveChangesAsync()
    {
        return base.SaveChangesAsync();
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value);
    }
}

ApplicationDbContext 继承自 ApplicationApiAuthorizationDbContext<ApplicationUser, ApplicationRole> 而不是 ApiAuthorizationDbContext<ApplicationUser>

public class ApplicationDbContext : ApplicationApiAuthorizationDbContext<ApplicationUser, ApplicationRole>

modelBuilder.Entity<ApplicationUser>()
    .HasMany(u => u.Roles)
    .WithMany(r => r.Users)
    .UsingEntity<IdentityUserRole<string>>(
        userRole => userRole.HasOne<ApplicationRole>()
            .WithMany()
            .HasForeignKey(ur => ur.RoleId)
            .IsRequired(),
        userRole => userRole.HasOne<ApplicationUser>()
            .WithMany()
            .HasForeignKey(ur => ur.UserId)
            .IsRequired());

然后您可以获得所有具有如下角色的用户:

var usersWithRoles = dbContext.Users.Include(x => x.Roles).ToList();