Why this violates the constraint of type parameter

2020-06-17 04:31发布

I am trying to customize asp.net identity entities with the following ones:

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>

I have then created a DbContext class like the following

public class AppDbContext : IdentityDbContext<User, Role, string, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>

And then configure these with

services
    .AddIdentity<User, Role>()
    .AddEntityFrameworkStores<AppDbContext>()
    .AddDefaultTokenProviders();

but I have tried also with

.AddEntityFrameworkStores<AppDbContext, string>()

I have read many blog posts on the internet, like this for example, but most of them have to deal against a change of data type for the keys, like to int or Guid. In my case I am not changing the default key datatype from string.

I all these cases the compile is ok but it throws an error when running

System.TypeLoadException GenericArguments[0],'MyIdentity.Entities.User', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`8[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken]' violates the constraint of type parameter 'TUser'.

at System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, int numGenericArgs, ObjectHandleOnStack type) at System.RuntimeTypeHandle.Instantiate(Type[] inst) at System.RuntimeType.MakeGenericType(Type[] instantiation)

If I add a customized UserStore class like explained in this post

public class UserStore : UserStore<User, Role, AppDbContext, string, UserClaim, UserRole, UserLogin, UserToken>

A compile error appear saying that

CS0311 The type 'MyIdentity.Entities.Role' cannot be used as type parameter 'TRole' in the generic type or method 'UserStore'. There is no implicit reference conversion from 'MyIdentity.Entities.Role' to 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole>'

What I am doing wrong?

3条回答
祖国的老花朵
2楼-- · 2020-06-17 04:45

I faced this issue, i found this: "When you are customizing ASP.NET Core Identity, you should not use AddEntityFrameworkStores anymore." you should implement yours:

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>

And Then redirect built-in services to your custom services (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>();

Then 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();
查看更多
霸刀☆藐视天下
3楼-- · 2020-06-17 05:00

The problem is here RoleClaim must be of type IdentityRoleClaim<TKey>

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

instead of TRoleClaim

where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>

It's reported here as a bug.

查看更多
叛逆
4楼-- · 2020-06-17 05:04
  • According to MSDN, the constraint for UserStore<TUser> is TUser : IdentityUser.
  • Also according to MSDN, IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>.
  • According to your declarations, User : IdentityUser<string, UserClaim, UserRole, UserLogin>.

The long names make it easy to miss, so let's alias IdentityUser as D and IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> as B. Now the above can be restated as:

  • TUser : D (a TUser must be a kind of D)
  • D : B (D is a kind of B)
  • User : B (your User is a kind of B in general but not a kind of D specifically, as required)
查看更多
登录 后发表回答