I'm building a completely custom AspNetCore.Identity Implementation because I want TKey
to be System.Guid
across the board. With respect, I have derived types for...
Role : IdentityRole<Guid, UserRole, RoleClaim>
RoleClaim : IdentityRoleClaim<Guid>
User : IdentityUser<Guid, UserClaim, UserRole, UserLogin>
UserClaim : IdentityUserClaim<Guid>
UserLogin : IdentityUserLogin<Guid>
UserRole : IdentityUserRole<Guid>
UserToken : IdentityUserToken<Guid>
ApplicationDbContext : IdentityDbContext<User, Role, Guid, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
ApplicationRoleManager : RoleManager<Role>
ApplicationRoleStore : RoleStore<Role, ApplicationDbContext, Guid, UserRole, RoleClaim>
ApplicationSignInManager : SignInManager<User>
ApplicationUserManager : UserManager<User>
**ApplicationUserStore** : UserStore<User, Role, ApplicationDbContext, Guid, UserClaim, UserRole, UserLogin, UserToken>
ApplicationUserStore
is the problem child!
Implementation
namespace NewCo.Identity { using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using System; public sealed class Role : IdentityRole<Guid, UserRole, RoleClaim> { } } namespace NewCo.Identity { using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using System; public sealed class UserRole : IdentityUserRole<Guid> { } } namespace NewCo.Identity { using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using System; public sealed class RoleClaim : IdentityRoleClaim<Guid> { } } // The problem is here... namespace NewCo.Identity { using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using System; using System.Security.Claims; public sealed class ApplicationUserStore : UserStore<User, Role, ApplicationDbContext, Guid, UserClaim, UserRole, UserLogin, UserToken> { } }
Error
The type 'NewCo.Identity.Role' cannot be used as type parameter 'TRole' in the generic type or method 'UserStore'. There is no implicit reference conversion from 'NewCo.Identity.Role' to 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole>'.
As far as I can see, unless this is some (co/contra/in)variance issue, all the code checks out...what did I get wrong?
Your ApplicationUserStore
needs the RoleClaim
at the end too (don't forget to update the related NuGet packages, otherwise you can't use these new additions):
ApplicationUserStore : UserStore< User, Role, ApplicationDbContext, Guid, UserClaim, UserRole, UserLogin, UserToken, RoleClaim>
Plus your ApplicationRoleStore
should provide how to create the RoleClaim
,
protected override RoleClaim CreateRoleClaim(Role role, Claim claim) { return new RoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value }; }
And also the ApplicationUserStore
should provide these mappings too:
protected override UserClaim CreateUserClaim(User user, Claim claim) { var userClaim = new UserClaim { UserId = user.Id }; userClaim.InitializeFromClaim(claim); return userClaim; } protected override UserLogin CreateUserLogin(User user, UserLoginInfo login) { return new UserLogin { UserId = user.Id, ProviderKey = login.ProviderKey, LoginProvider = login.LoginProvider, ProviderDisplayName = login.ProviderDisplayName }; } protected override UserRole CreateUserRole(User user, Role role) { return new UserRole { UserId = user.Id, RoleId = role.Id }; } protected override UserToken CreateUserToken(User user, string loginProvider, string name, string value) { return new UserToken { UserId = user.Id, LoginProvider = loginProvider, Name = name, Value = value }; }
Then redirect built-in services to your custom services:
services.AddScoped<UserStore<User, Role, ApplicationDbContext, int, UserClaim, UserRole, UserLogin, UserToken, RoleClaim>, ApplicationUserStore>(); services.AddScoped<UserManager<User>, ApplicationUserManager>(); services.AddScoped<RoleManager<Role>, ApplicationRoleManager>(); services.AddScoped<SignInManager<User>, ApplicationSignInManager>(); services.AddScoped<RoleStore<Role, ApplicationDbContext, int, UserRole, RoleClaim>, ApplicationRoleStore>(); services.AddScoped<IEmailSender, AuthMessageSender>(); services.AddScoped<ISmsSender, AuthMessageSender>();
now introduce your custom services:
services.AddIdentity<User, Role>(identityOptions => { // ... }).AddUserStore<ApplicationUserStore>() .AddUserManager<ApplicationUserManager>() .AddRoleStore<ApplicationRoleStore>() .AddRoleManager<ApplicationRoleManager>() .AddSignInManager<ApplicationSignInManager>() // You **cannot** use .AddEntityFrameworkStores() when you customize everything //.AddEntityFrameworkStores<ApplicationDbContext, int>() .AddDefaultTokenProviders();