为什么这违反了类型参数'TUser'的约束?

问题描述 投票:0回答:4

我正在尝试使用以下内容自定义 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 asp.net-identity asp.net-identity-3
4个回答
14
投票

我遇到了这个问题,我发现了这个:“当你自定义 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();

10
投票
  • 根据 MSDN
    UserStore<TUser>
    的约束是
    TUser : IdentityUser
  • 也根据MSDN
    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
    ,根据需要)

3
投票

问题就在这里

RoleClaim
必须是类型
IdentityRoleClaim<TKey>

 where TRole : IdentityRole<TKey, TUserRole, IdentityRoleClaim<TKey>>

而不是

TRoleClaim

where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>

此处报告为错误


0
投票

在 .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);
等,角色在这里是一个字符串

© www.soinside.com 2019 - 2024. All rights reserved.