多用户类型标识 - DbContext 设计

Posted

技术标签:

【中文标题】多用户类型标识 - DbContext 设计【英文标题】:Multiple user type Identity - DbContext design 【发布时间】:2020-03-11 00:35:14 【问题描述】:

我正在尝试将 .NET Core 的 Identity 包与扩展 IdentityUser<Guid> 的多个类一起使用,但使用单个 UserRole 类。

我有多个为每个用户类型扩展 UserStore<T> 的类和一个扩展 RoleStore<UserRole> 的类。

以下是我的startup.cs

services.AddIdentity<InternalUser, UserRole>(IdentityOptions)
    .AddDefaultTokenProviders()
    .AddUserStore<InternalUserStore>()
    .AddRoleStore<GenericUserRoleStore>();

services.AddIdentityCore<Contractor>(IdentityOptions)
    .AddRoles<UserRole>()
    .AddDefaultTokenProviders()
    .AddUserStore<ContractorUserStore>()
    .AddRoleStore<GenericUserRoleStore>();

services.AddIdentityCore<Homeowner>(IdentityOptions)
    .AddRoles<UserRole>()
    .AddDefaultTokenProviders()
    .AddUserStore<HomeownerUserStore>()
    .AddRoleStore<GenericUserRoleStore>();

我的DbContext 没有扩展IdentityDbContext

public sealed class EntityDbContext: DbContext  

我遇到了多个错误,因此我将以下内容添加到 DbContext 但我将其注释掉了:

public DbSet<IdentityUserClaim<Guid>> UserClaims  get; set; 

public DbSet<IdentityUserRole<Guid>> UserRoles  get; set; 

我遇到了许多不同的错误:

实例“Dal.IdentityStores.InternalUserStore”上的构建错误 对于 PluginType IUserStore - 以及 PluginType Microsoft.AspNetCore.Identity.RoleManager1[Models.Entities.Users.UserRole] - and Instance 'Dal.IdentityStores.GenericUserRoleStore' for PluginType Microsoft.AspNetCore.Identity.IRoleStore1[Models.Entities.Users.UserRole] 的实例“RoleManager” - 以及 PluginType 的实例“Dal.IdentityStores.GenericUserRoleStore” Microsoft.AspNetCore.Identity.IRoleStore1[Models.Entities.Users.UserRole] - and Instance 'Dal.IdentityStores.ContractorUserStore' for PluginType Microsoft.AspNetCore.Identity.IUserStore1[Models.Entities.Contractors.Contractor] - 以及 PluginType 的实例“UserClaimsPrincipalFactory” Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[Models.Entities.Contractors.Contractor] - and Instance 'UserClaimsPrincipalFactory<Contractor, UserRole>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[Models.Entities.Contractors.Contractor] - 以及 PluginType Microsoft.AspNetCore.Identity.UserManager1[Models.Entities.Homeowners.Homeowner] - and Instance 'UserClaimsPrincipalFactory<Homeowner>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[Models.Entities.Homeowners.Homeowner] 的实例“UserManager”

这是link to my repo

【问题讨论】:

那么,你的问题是什么? 我的代码不起作用。我收到上述错误。我该如何修复它以使用多种类型作为“用户”。 我已经克隆了你的仓库,它对我有用,所以可能是你的环境问题。 @VanoMaisuradze 尝试创建用户并登录。你会看到问题。有一个用于注册/登录的控制器 是的,我做到了。谢谢 【参考方案1】:

我复制了您的问题,下面是一个解决方案,但我会再次考虑为不同的用户角色创建多个表。

以下是针对多个用户表的两个主要原因:

    当您想通过 id 查找用户时(假设您不知道角色),您将需要针对不同的表运行多个查询,这会降低性能并增加代码的复杂性。 这也可能会增加数据库的复杂性,因为您需要为其他表设置多个外键。

如果您仍想为不同的用户角色创建多个表,这里有一个小“技巧”​​。您只需要重写 OnModelCreating 方法并配置实体:

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

        builder.Entity<Contractor>(b =>
        
            b.HasMany<IdentityUserRole<Guid>>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
        );

        builder.Entity<UserRole>(b =>
        
            b.HasKey(r => r.Id);
            b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex").IsUnique();
            b.ToTable("AspNetRoles");
            b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();

            b.Property(u => u.Name).HasMaxLength(256);
            b.Property(u => u.NormalizedName).HasMaxLength(256);

            b.HasMany<IdentityUserRole<Guid>>().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
            b.HasMany<IdentityRoleClaim<Guid>>().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
        );

        builder.Entity<IdentityRoleClaim<Guid>>(b =>
        
            b.HasKey(rc => rc.Id);
            b.ToTable("AspNetRoleClaims");
        );

        builder.Entity<IdentityUserRole<Guid>>(b =>
        
            b.HasKey(r => new  r.UserId, r.RoleId );
            b.ToTable("AspNetUserRoles");
        );

        builder.Entity<UserRole>().ToTable("Roles");
        builder.Entity<IdentityUserRole<Guid>>().ToTable("UserRoles");
        builder.Entity<IdentityRoleClaim<Guid>>().ToTable("RoleClaims");
        builder.Entity<IdentityUserClaim<Guid>>().ToTable("UserClaims");
    

之后,您应该可以登录了。

【讨论】:

【参考方案2】:

根据 OP 在 cmets 中解释的要求以及 OP 说他/她没有数据库设计经验,我将尝试提供一个答案,希望有助于使 OP 的项目和软件开发生涯更轻松:

首先,您需要一个“用户”表:

|---------------------|------------------|------------------|
|      FirstName      |     LastName     |       Role       |
|---------------------|------------------|------------------|
|          Joe        |       Black      |     Contractor   |
|---------------------|------------------|------------------|

您对项目的多个用户表没有要求,一般来说,任何项目或软件产品也不需要这样的东西。当/IF 用户表扩展到 N 数十亿条记录时,它将跨分区分片以提高性能,和/或针对读取与写入进行非规范化,但在许多扩展的复杂软件系统设计中,它在逻辑上仍然是一张表。

你说:

如何为不同类型的用户提供不同的用户元数据? 承包商有自己的财产,房主有自己的一套 属性等。我不想有一张巨大的桌子 可能的属性

您担心的是您最终不会得到一张巨大的桌子。简而言之,这与您无关。您还没有一张大桌子,如果您添加了 30 列(如您所说的“属性”),那将不是一张大桌子。

创建单个用户表。为特定承包商属性添加可空列,为特定房主属性添加可空列。当属性适用于所有用户时,使列不为空。

适用于所有用户的列是您的用户的角色列,因此您有一个不可为空的列,称为“角色”

注意,如果您使用的是关系数据库,则不需要创建包含 Role 行记录的 Role 表。您可以这样做,并且您的课程讲师可能会期望它,但您也可以在后端创建一个 const 字符串类或枚举来表示可能的角色,这是一种为这些角色提供完整性的持久性形式。我之所以提到这一点,是因为在这个领域,开发人员很快就会在关系数据库设计中过度使用引用完整性。

【讨论】:

抱歉,您的回答是我已经提出的建议。这并不能直接回答 OP 的问题。此外,创建可为空的列可能不是一个好的解决方案。例如。使用 Json 可能会更干净。 不,这不仅仅是一个建议,它是一种措辞礼貌的正确方法来处理和实施解决方案。

以上是关于多用户类型标识 - DbContext 设计的主要内容,如果未能解决你的问题,请参考以下文章

将 ASP.NET 标识集成到现有 DbContext 中

SQL Server 2008 - 返回连接条目和“无法绑定多部分标识符”错误的用户定义函数

Linux下用户账号和权限管理

Laravel 4 多用户身份验证

C基础 多用户分级日志库 sclog

流程中心使用详情表单控件