我正在尝试使用以下内容自定义 asp.net 身份实体:
public class User : IdentityUser<string, UserClaim, UserRole, UserLogin>
public class UserClaim : IdentityUserClaim<string>
public class UserLogin : IdentityUserLogin<string>
public class UserRole : IdentityUserRole<string>
public class UserToken : IdentityUserToken<string>
public class Role : IdentityRole<string, UserRole, RoleClaim>
public class RoleClaim : IdentityRoleClaim<string>
然后我创建了一个如下所示的 DbContext 类
public class AppDbContext : IdentityDbContext<User, Role, string, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
然后使用
配置这些services
.AddIdentity<User, Role>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
但我也尝试过
.AddEntityFrameworkStores<AppDbContext, string>()
我在互联网上读过很多博客文章,例如this,但大多数都必须处理键数据类型的更改,例如
int
或Guid
。就我而言,我不会更改默认键数据类型 string
。
我在所有这些情况下编译都可以,但运行时会抛出错误
系统.TypeLoadException GenericArguments[0],'MyIdentity.Entities.User', 上 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`8[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken]' 违反了类型参数“TUser”的约束。
在 System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle 句柄, IntPtr* pInst、int numGenericArgs、ObjectHandleOnStack 类型)位于 System.RuntimeTypeHandle.Instantiate(Type[] inst) 在 System.RuntimeType.MakeGenericType(Type[] 实例化)
如果我添加自定义的
UserStore
类,如这篇文章中所述
public class UserStore : UserStore<User, Role, AppDbContext, string, UserClaim, UserRole, UserLogin, UserToken>
出现编译错误,指出
CS0311 类型“MyIdentity.Entities.Role”不能用作类型 通用类型或方法“UserStore”中的参数“TRole”。没有隐式引用转换 'MyIdentity.Entities.Role' 至 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole>'
我做错了什么?
我遇到了这个问题,我发现了这个:“当你自定义 ASP.NET Core Identity 时,你不应该再使用 AddEntityFrameworkStores。” 你应该实现你的:
public class ApplicationRoleManager: RoleManager<Role>
public class ApplicationRoleStore : RoleStore<Role, ApplicationDbContext, int, UserRole, RoleClaim>
public class ApplicationSignInManager : SignInManager<User>
public class ApplicationUserManager : UserManager<User>
public class ApplicationUserStore : UserStore<User, Role, ApplicationDbContext, int, UserClaim, UserRole, UserLogin, UserToken, RoleClaim>
然后将内置服务重定向到您的自定义服务(Startup.cs):
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>();
然后介绍您的定制服务:
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();
UserStore<TUser>
的约束是 TUser : IdentityUser
。IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
。User : IdentityUser<string, UserClaim, UserRole, UserLogin>
。名称太长,很容易错过,所以我们将
IdentityUser
别名为 D
,将 IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
别名为 B
。现在以上可以重述为:
TUser : D
(TUser
必须是D
的一种)D : B
(D
是B
的一种)User : B
(您的User
一般是一种B
,但不是具体的D
,根据需要)问题就在这里
RoleClaim
必须是类型IdentityRoleClaim<TKey>
where TRole : IdentityRole<TKey, TUserRole, IdentityRoleClaim<TKey>>
而不是
TRoleClaim
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
在 .Net 8 中对我有用的是: 我需要将主键从字符串(默认)更改为 Guid。所以:ApplicationUser.cs
public class ApplicationUser : IdentityUser<Guid> {
public string? Name { get; set; }
public byte[]? Picture { get; set; }
}
ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser , IdentityRole<Guid> , Guid> {
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) {
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
}
}
所以我还需要将 IdentityRole 键从字符串更改为 Guid,但我不想更改 IdentityRole 中的任何内容。所以在 Program.cs 中我写道:
builder.Services.AddIdentity<ApplicationUser, IdentityRole<Guid>>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
我在 Program.cs 中看到了这样做的答案:(至少在 .Net 8 中这是错误的)
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders();
当然之后你需要像这样使用IdentityRole:
private readonly RoleManager<IdentityRole<Guid>> roleManager;
new IdentityRole<Guid>(role);
等,角色在这里是一个字符串