多用户类型标识 - 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.RoleManager
1[Models.Entities.Users.UserRole] - and Instance 'Dal.IdentityStores.GenericUserRoleStore' for PluginType Microsoft.AspNetCore.Identity.IRoleStore
1[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.IUserStore
1[Models.Entities.Contractors.Contractor] - 以及 PluginType 的实例“UserClaimsPrincipalFactory” Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[Models.Entities.Contractors.Contractor] - and Instance 'UserClaimsPrincipalFactory<Contractor, UserRole>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory
1[Models.Entities.Contractors.Contractor] - 以及 PluginType Microsoft.AspNetCore.Identity.UserManager1[Models.Entities.Homeowners.Homeowner] - and Instance 'UserClaimsPrincipalFactory<Homeowner>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory
1[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 设计的主要内容,如果未能解决你的问题,请参考以下文章