【问题标题】:What is the right way to extend AspnetUserRoles table in ASPNET.Core 3.1?在 ASPNET.Core 3.1 中扩展 AspnetUserRoles 表的正确方法是什么?
【发布时间】:2020-09-20 09:45:46
【问题描述】:

情况

我正在使用带有 Angular 8 模板的 Identity ASP.NET Core 3.1。我想扩展 ASPNETUserRoles 表并在其中添加另一个自定义键列 CompanyId。

默认身份提供:

public virtual TKey UserId { get; set; }
public virtual TKey RoleId { get; set; }

当我将我的 DbContext 从 UserId (string) 修改为 UserId (long) 时,DbContext 看起来像:

public class CompanyDBContext : KeyApiAuthorizationDbContext<User, Role, UserRole, long>
{
    public CompanyDBContext(
        DbContextOptions options,
        IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    public DbSet<Company> Companies { get; set; }

}

KeyApiAuthorizationDbContext

public class KeyApiAuthorizationDbContext<TUser, TRole, IdentityUserRole, TKey> : IdentityDbContext<TUser, TRole, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>, IdentityUserLogin<TKey>, IdentityRoleClaim<TKey>, IdentityUserToken<TKey>>, IPersistedGrantDbContext
    where TUser : IdentityUser<TKey>
    where TRole : IdentityRole<TKey>
    where IdentityUserRole : IdentityUserRole<TKey>
    where TKey : IEquatable<TKey>
{
    private readonly IOptions<OperationalStoreOptions> _operationalStoreOptions;

    /// <summary>
    /// Initializes a new instance of <see cref="ApiAuthorizationDbContext{TUser, TRole, TKey}"/>.
    /// </summary>
    /// <param name="options">The <see cref="DbContextOptions"/>.</param>
    /// <param name="operationalStoreOptions">The <see cref="IOptions{OperationalStoreOptions}"/>.</param>
    public KeyApiAuthorizationDbContext(
        DbContextOptions options,
        IOptions<OperationalStoreOptions> operationalStoreOptions)
        : base(options)
    {
        _operationalStoreOptions = operationalStoreOptions;
    }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{PersistedGrant}"/>.
    /// </summary>
    public DbSet<PersistedGrant> PersistedGrants { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{DeviceFlowCodes}"/>.
    /// </summary>
    public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }

    Task<int> IPersistedGrantDbContext.SaveChangesAsync() => base.SaveChangesAsync();

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

实体

public class User : IdentityUser<long> {}
public class Role : IdentityRole<long> {}
public class UserRole : IdentityUserRole<long>
{
    public long CompanyId { get; set; }
}

出现问题

当我注册我的用户并返回 true 时,我将在 UserRole 表中添加一个当前用户,如下所示,但是当我的调试器到达 await _context.SaveChangesAsync(); 方法时,它向我显示了一个异常

if (result.Succeeded)
{
    foreach (var role in model.Roles.Where(x => x.IsChecked = true))
    {
        var entity = new Core.Entities.Identity.UserRole()
        {
            UserId = model.User.Id,
            RoleId = role.Id,
            CompanyId = companycode
        };

            _context.UserRoles.Add(entity);
    }
    await _context.SaveChangesAsync();

}

我不知道我在哪里犯错?如果上述覆盖用户角色的步骤是错误的,那么请协助我。

我还分享了我的迁移详细信息以供您参考,这可能是我做错了。

迁移

migrationBuilder.CreateTable(
name: "Companies",
columns: table => new
{
   Id = table.Column<long>(nullable: false)
       .Annotation("SqlServer:Identity", "1, 1"),
                Name = table.Column<string>(nullable: false),
                Code = table.Column<string>(nullable: false),
                Logo = table.Column<string>(nullable: true)
       },
       constraints: table =>
       {
                table.PrimaryKey("PK_Companies", x => x.Id);
});


migrationBuilder.CreateTable(
                name: "AspNetUserRoles",
                columns: table => new
                {
                    UserId = table.Column<long>(nullable: false),
                    RoleId = table.Column<long>(nullable: false),
                    CompanyId = table.Column<long>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId, x.CompanyId });
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
                        column: x => x.RoleId,
                        principalTable: "AspNetRoles",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_Companies_CompanyId",
                        column: x => x.CompanyId,
                        principalTable: "Companies",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

迁移快照

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int")
                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

                    b.Property<string>("ClaimType")
                        .HasColumnType("nvarchar(max)");

                    b.Property<string>("ClaimValue")
                        .HasColumnType("nvarchar(max)");

                    b.Property<long>("RoleId")
                        .HasColumnType("bigint");

                    b.HasKey("Id");

                    b.HasIndex("RoleId");

                    b.ToTable("AspNetRoleClaims");
                });

【问题讨论】:

  • 为什么不在用户上添加公司?你的用例是什么需要公司 ID 作为角色
  • @EduCielo 因为我在企业级管理用户,因此我想为此使用 aspnetuserroles。我不想再创建一个同时管理 UserId 和 CompanyId 的表。
  • 在我的情况下我没有收到此错误“无效的列鉴别器”
  • docs.microsoft.com/en-us/aspnet/core/security/authentication/… 这包括正确的配置。我没有看到您在模型构建器上将关系配置到公司实体中

标签: asp.net-mvc entity-framework asp.net-core entity-framework-core asp.net-core-identity


【解决方案1】:

现在我想出了我自己问题的答案。

回忆问题

我想扩展用户角色 (IdentityUserRole) 并添加一些外键,但遇到两个错误:

错误 1

列名“鉴别器”无效

错误 2

无法在“UserRole”上配置密钥,因为它是派生类型。必须在根类型“IdentityUserRole”上配置密钥。如果您不打算将“IdentityUserRole”包含在模型中,请确保它不包含在上下文的 DbSet 属性中、在对 ModelBuilder 的配置调用中引用或从包含的类型的导航属性中引用在模型中。

这是因为如果你查看 IdentityUserRole 的定义,你会发现它已经有了主键:

public virtual TKey UserId { get; set; }
public virtual TKey RoleId { get; set; }

那么,解决办法是什么?

要解决这个问题,我们必须重写用户角色实现。

现在如果你看到代码:

public class CompanyDBContext : IdentityDbContext<User, Role, long, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>, IPersistedGrantDbContext
    {
        private readonly IOptions<OperationalStoreOptions> _operationalStoreOptions;
        public CompanyDBContext(
            DbContextOptions options,
            IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options)
        {
            _operationalStoreOptions = operationalStoreOptions;
        }

        Task<int> IPersistedGrantDbContext.SaveChangesAsync() => base.SaveChangesAsync();

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value);
            modelBuilder.Entity<UserRole>(b =>
            {
                b.HasKey(ur => new { ur.UserId, ur.RoleId, ur.CompanyId });
            });
        public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
        public DbSet<PersistedGrant> PersistedGrants { get; set; }

    }

就是这样。正如@EduCielo 建议的那样,我重新考虑我的代码并进行正确的映射和配置,并覆盖模型构建器以映射用户角色的正确关系。

对于新开发人员,我正在共享我在上面使用的实体类,以使其与您的代码完全兼容。

public class User : IdentityUser<long> { //Add new props to modify ASP.NETUsers Table }

public class Role : IdentityRole<long> { }

public class UserClaim: IdentityUserClaim<long> { }

public class UserRole : IdentityUserRole<long>
{

    [Key, Column(Order = 2)]
    public long CompanyId { get; set; }

    public Company Company { get; set; }
}

public class UserLogin: IdentityUserLogin<long> { }

public class RoleClaim : IdentityRoleClaim<long> { }

public class UserToken : IdentityUserToken<long> { }

结论

add-migration FirstMigration -context CompanyDbContext
update-database -context CompanyDbContext

快乐编码:)

Ref Link

【讨论】:

    【解决方案2】:

    从 IdentityRole 继承

     public class myRoles: IdentityRole
      {
        public bool ACTIVE { get; set; }
        public bool LOCKED { get; set; }
        public int SORT_ORDER { get; set; }
        public bool DEFAULT_FLAG { get; set; }
        public string USER_PRIVILEGES { get; set; }
        public string CREATED_BY { get; set; }
        public DateTime? CREATED_DATE { get; set; }
        public string UPDATED_BY { get; set; }
        public DateTime? UPDATED_DATE { get; set; }
    
    }
    

    在 startUp 中引用您的角色类

    services.AddIdentity<RegisterUser, myRoles>
                .AddEntityFrameworkStores<ContextClass>()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-18
      • 2020-02-13
      • 2017-09-14
      • 1970-01-01
      • 2018-05-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多