Reputation: 1592
I'm trying to use the Identity package of .NET Core with multiple classes that extend IdentityUser<Guid>
but with a single UserRole
class.
I have multiple classes that extend UserStore<T>
for each user type and a single class that extends RoleStore<UserRole>
.
The following is my 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>();
My DbContext
is not extending IdentityDbContext
:
public sealed class EntityDbContext: DbContext { }
I was getting multiple errors so I added the following to DbContext
but I commented it out:
public DbSet<IdentityUserClaim<Guid>> UserClaims { get; set; }
public DbSet<IdentityUserRole<Guid>> UserRoles { get; set; }
I'm getting many different errors:
build Error on Instance 'Dal.IdentityStores.InternalUserStore' for PluginType IUserStore - and Instance 'RoleManager' for 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] - and Instance 'Dal.IdentityStores.GenericUserRoleStore' for PluginType Microsoft.AspNetCore.Identity.IRoleStore1[Models.Entities.Users.UserRole] - and Instance 'Dal.IdentityStores.ContractorUserStore' for PluginType Microsoft.AspNetCore.Identity.IUserStore
1[Models.Entities.Contractors.Contractor] - and Instance 'UserClaimsPrincipalFactory' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[Models.Entities.Contractors.Contractor] - and Instance 'UserClaimsPrincipalFactory<Contractor, UserRole>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory
1[Models.Entities.Contractors.Contractor] - and Instance 'UserManager' for PluginType Microsoft.AspNetCore.Identity.UserManager1[Models.Entities.Homeowners.Homeowner] - and Instance 'UserClaimsPrincipalFactory<Homeowner>' for PluginType Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory
1[Models.Entities.Homeowners.Homeowner]
This is the link to my repo
Upvotes: 8
Views: 2201
Reputation: 19232
Based on the requirements the OP explained in the comments and that the OP said he/she does not have database design experience, I will attempt to provide an answer that hopefully helps make life easier for the OP's project and software development career:
First off, you want a single "Users" table:
|---------------------|------------------|------------------|
| FirstName | LastName | Role |
|---------------------|------------------|------------------|
| Joe | Black | Contractor |
|---------------------|------------------|------------------|
You have no requirements for multiple User tables for the project, nor would any project or software product require such a thing generally speaking. When/IF a User table scales to N number billions of records, than it would be sharded across partitions for performance, and/or denormalized for reads versus writes, but it would still be logically one table in many scaled complex software system designs.
You state:
How can I have different user metadata for different types of a user? A contractor has it's own properties, a homeowner has its own set of properties and etc. I don't want to have a giant table with all possible properties
A concern of yours is that you don't end up with a giant table. In short, this is not a concern for you. You don't have a giant table yet and if you added 30 columns ("properties" as you said) that would NOT be a giant table.
Create a single User table. Add nullable columns for the specific contractor properties and nullable columns for the specific homeowner properties. Make the columns Not Null when the property applies to all users.
A column that will apply to all users is the Role column for your Users, so you have a Not Nullable column called "Role"
Note, if you are using a relational database, you do not need to create a Role table that contains the Role row records. You can do this and your course instructor may expect it but you can also create a const strings class or enum in your backend that represents the possible Roles and that is a form of persistence that provides integrity for those roles. I mention this because this is an area where developers quickly go into referential integrity overkill with relational database design.
Upvotes: 1
Reputation: 5909
I reproduced your problem and below is a solution to it, but I would think again about creating multiple tables for different user roles.
Here are two main reasons against multiple user tables:
In case you still want to have multiple tables for different user roles, here is a little "hack". You just need to override OnModelCreating method and configure entities:
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");
}
After that, you should be able to login.
Upvotes: 5