.NET Core 2.1 Identity为所有用户提供相关角色

And*_*iss 26 c# mysql entity-framework-core asp.net-core asp.net-core-identity

我正在尝试为用户管理管理页面提取所有Identity用户及其相关角色.我认为这相当容易,但显然不是.我尝试过以下解决方案:https://stackoverflow.com/a/43562544/5392786但到目前为止还没有解决.

这是我到目前为止:

ApplicationUser:

public class ApplicationUser : IdentityUser
{
    public List<IdentityUserRole<string>> Roles { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

的DbContext

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

启动标识代码

services.AddIdentity<ApplicationUser, IdentityRole>(options => options.Stores.MaxLengthForKeys = 128)
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
Run Code Online (Sandbox Code Playgroud)

我想要显示列表的Razor Page:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

调用时出现以下错误userManager.Users.Include(u => u.Roles).ToList();:

MySql.Data.MySqlClient.MySqlException:'字段列表'中的'未知列'u.Roles.ApplicationUserId'

And*_*iss 47

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

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

ApplicationUser

public class ApplicationUser : IdentityUser
{
    public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

ApplicationUserRole

public class ApplicationUserRole : IdentityUserRole<string>
{
    public virtual ApplicationUser User { get; set; }
    public virtual ApplicationRole Role { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

ApplicationRole

public class ApplicationRole : IdentityRole
{
    public ICollection<ApplicationUserRole> UserRoles { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

的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();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

启动

services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.Stores.MaxLengthForKeys = 128)
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
Run Code Online (Sandbox Code Playgroud)

最后,确保当你使用它时,你急切地加载用户的UserRoles,然后像UserRole的角色一样:

this.Users = userManager.Users.Include(u => u.UserRoles).ThenInclude(ur => ur.Role).ToList();
Run Code Online (Sandbox Code Playgroud)

我有一个问题,其中Role每个的属性UserRole为null,这通过添加.ThenInclude(ur => ur.Role)部分解决.

Microsoft doc on multi-level eager loading:https://docs.microsoft.com/en-us/ef/core/querying/related-data#including-multiple-levels

  • 为了避免错误:"无法为'IdentityUserRole <Guid>'创建DbSet,因为此类型未包含在上下文的模型中",我将您的代码更改为:services.AddIdentity <User,Role>().AddEntityFrameworkStores <AppDbContext >().AddDefaultTokenProviders().AddUserStore <UserStore <User,Role,AppDbContext,Guid,IdentityUserClaim <Guid>,UserRole,IdentityUserLogin <Guid>,IdentityUserToken <Guid>,IdentityRoleClaim <Guid >>>().AddRoleStore <RoleStore <角色,AppDbContext,Guid,UserRole,IdentityRoleClaim <Guid >>>(); (3认同)
  • 是的,我做到了。我所做的与您的示例中显示的完全相同。但是由于某种原因不能为我工作。 (3认同)
  • 与@MuhammadHannan 有同样的问题,使用 ```ASP.NET Core 2.2.2```。```实体类型'IdentityUserRole&lt;string&gt;'需要定义一个主键```。现在放弃了,不知道怎么解决。 (3认同)
  • 嘿@Andy,我收到“实体类型‘IdentityUserRole&lt;string&gt;’需要定义一个主键。”带有“Identity v2.2.0”。任何的想法? (2认同)
  • @ChristopheGigax 而不是 `builder.Entity&lt;ApplicationUserRole&gt;().HasKey(x =&gt; new { x.UserId, x.RoleId });` 你需要 `builder.Entity&lt;IdentityUserRole&lt;string&gt;&gt;().HasKey(x =&gt; 新的 { x.UserId, x.RoleId });` (2认同)
  • 它在.Net Core 5.0 中不起作用。一切都会编译,但 .net 向“AspNetUserRoles”添加了名为“UserId1”和“RoleId1”的附加列,因此角色在用户实例中不可见。我真的更喜欢这个解决方案,但我投票支持“-1”,直到这个问题得到解决。只是因为我花了3个小时检查它是否有效。 (2认同)

jst*_*tur 8

对于 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);
Run Code Online (Sandbox Code Playgroud)

生成的 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"
Run Code Online (Sandbox Code Playgroud)


Dre*_*per 7

更新:此解决方案适用于 EF Core 5,但似乎从来不应该这样做,并且在 EF Core 6 中不再可能。

您可以使用 EF Core 5.0 多对多功能,并避免子类化 IdentityUserRole/IdentityRole。

应用程序用户

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

public class ApplicationUser : IdentityUser
{
    public ICollection<IdentityRole> Roles { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

数据库上下文:

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

Run Code Online (Sandbox Code Playgroud)


小智 5

参考评论

首先是获取数据的代码

 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;
        }
Run Code Online (Sandbox Code Playgroud)

在 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; }
    }
Run Code Online (Sandbox Code Playgroud)

最后

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);
        }
Run Code Online (Sandbox Code Playgroud)

结果将是用户名和用户角色。如果用户有 1 个以上的角色,则数据将显示为管理员、编辑器等...

完整的代码可以在这里找到 这里这里 希望这有帮助。


Moh*_*ufa 5

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


    }
Run Code Online (Sandbox Code Playgroud)

祝好运


Ogg*_*las 5

更新:

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

CS0535“ApplicationApiAuthorizationDbContext <TUser,TRole>”未实现接口成员“IPersistedGrantDbContext.ServerSideSessions”

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

    public DbSet<ServerSideSession> ServerSideSessions
    {
        get;
        set;
    }
Run Code Online (Sandbox Code Playgroud)

但这导致了以下错误endpoints.MapRazorPages();

System.Reflection.ReflectionTypeLoadException:“无法加载一种或多种请求的类型。程序集“Microsoft.AspNetCore.ApiAuthorization.IdentityServer,Version=6.0.5.0,Culture=neutral,PublicKeyToken=adb9793829ddae60”中类型“Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext`1”中的方法“get_ServerSideSessions”没有实现。 '

建议继续坚持下去,Duende.IdentityServer.EntityFramework.Storage 5.2.0直到这个问题得到解决。

原来的:

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

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

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

EF Core 7.0 中可能会再次通过影子导航添加单向多对多关系的支持,但尚未完成:

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

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

应用用户:

public class ApplicationUser : IdentityUser
{
    public ICollection<ApplicationRole> Roles { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

应用角色:

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

}
Run Code Online (Sandbox Code Playgroud)

Program.cs或者Startup.cs

services.AddDefaultIdentity<ApplicationUser>(options =>
options.SignIn.RequireConfirmedAccount = false)
    .AddRoles<ApplicationRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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());
Run Code Online (Sandbox Code Playgroud)

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

var usersWithRoles = dbContext.Users.Include(x => x.Roles).ToList();
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述