Skip to content
This repository has been archived by the owner on Dec 20, 2018. It is now read-only.

Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore violates the constraint of type parameter 'TUser'. #1082

Closed
MaklaCof opened this issue Jan 18, 2017 · 12 comments

Comments

@MaklaCof
Copy link

I am trying to use Id as int (Identity). My version is 1.1
devenv_2017-01-18_13-09-21

I override all Identity classes:

    [Table("Roles")]
    public partial class AppRole : IdentityRole<int, AppUserRole, AppRoleClaim>
    {
    }
    [Table("RoleClaims")]
    public partial class AppRoleClaim : IdentityRoleClaim<int>
    {
    }
    [Table("Users")]
    public partial class AppUser : IdentityUser<int, AppUserClaim, AppUserRole, AppUserLogin>
    {       
    }
    [Table("UserClaims")]
    public partial class AppUserClaim : IdentityUserClaim<int>
    {
    }
    [Table("UserLogins")]
    public partial class AppUserLogin : IdentityUserLogin<int>
    {
        [Key]
        [Required]
        public int Id { get; set; }
    }
    [Table("UserRoles")]
    public partial class AppUserRole : IdentityUserRole<int>
    {
    }
    [Table("UserTokens")]
    public partial class AppUserToken : IdentityUserToken<int>
    {
        [Key]
        [Required]
        public int Id { get; set; }
    }

and my context is:

    public partial class AppDbContext : IdentityDbContext<AppUser, AppRole, int, AppUserClaim, AppUserRole, AppUserLogin, AppRoleClaim, AppUserToken>
    {

        public AppDbContext() : base() {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"...");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<AppRole>(entity =>
            {
                entity
                .HasKey(u => u.Id);
                entity.Property(p => p.Id)
                .ValueGeneratedOnAdd();
            });

            modelBuilder.Entity<AppRoleClaim>(entity =>
            {
                entity
                .HasKey(u => u.Id);
                entity.Property(p => p.Id)
                .ValueGeneratedOnAdd();
            });

            modelBuilder.Entity<AppUser>(entity =>
            {
                entity
                .HasKey(u => u.Id);
                entity.Property(p => p.Id)
                .ValueGeneratedOnAdd();
            });

            modelBuilder.Entity<AppUserClaim>(entity =>
            {
                entity
                .HasKey(u => u.Id);
                entity.Property(p => p.Id)
                .ValueGeneratedOnAdd();
            });

            modelBuilder.Entity<AppUserLogin>(entity =>
            {
                entity
                .HasKey(u => u.Id);
                entity.Property(p => p.Id)
                .ValueGeneratedOnAdd();
            });

            modelBuilder.Entity<AppUserRole>(entity =>
            {
                entity
                .HasKey(u => new { u.RoleId, u.UserId });
            });

            modelBuilder.Entity<AppUserToken>(entity =>
            {
                entity
                .HasKey(u => u.Id);
                entity.Property(p => p.Id)
                .ValueGeneratedOnAdd();
            });
        }
    }

and startup.cs:

                services.AddMvc();

                services.AddEntityFrameworkSqlServer();

                var connection = Configuration["ConnectionStrings"];
                services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connection));

                var res = services
                    .AddIdentity<AppUser, AppRole>(config =>
                    {
                        config.User.RequireUniqueEmail = true;
                        config.Password.RequireNonAlphanumeric = false;
                        config.Cookies.ApplicationCookie.AutomaticChallenge = false;
                    })
                    .AddUserStore<ApplicationUserStore>()
                .AddEntityFrameworkStores<AppDbContext, int>()
                .AddDefaultTokenProviders();

I found few web-pages describing this error and all suggest upgrading to 1.1.
Exceptions is raised when debugging in line .AddEntityFrameworkStores<AppDbContext, int>()
My complete error displayed in page is:

TypeLoadException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type parameter 'TUser'.
System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, int numGenericArgs, ObjectHandleOnStack type)

ArgumentException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[TUser,TRole,TContext,TKey]' violates the constraint of type 'TUser'.
System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)

TypeLoadException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type parameter 'TUser'.
System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, int numGenericArgs, ObjectHandleOnStack type)
System.RuntimeTypeHandle.Instantiate(Type[] inst)
System.RuntimeType.MakeGenericType(Type[] instantiation)

Show raw exception details
ArgumentException: GenericArguments[0], 'OpPISWeb.Models.AppUser', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[TUser,TRole,TContext,TKey]' violates the constraint of type 'TUser'.
System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
System.RuntimeType.MakeGenericType(Type[] instantiation)
Microsoft.Extensions.DependencyInjection.IdentityEntityFrameworkBuilderExtensions.GetDefaultServices(Type userType, Type roleType, Type contextType, Type keyType)
Microsoft.Extensions.DependencyInjection.IdentityEntityFrameworkBuilderExtensions.AddEntityFrameworkStores<TContext, TKey>(IdentityBuilder builder)
OpPISWeb.Startup.ConfigureServices(IServiceCollection services)
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()

Show raw exception details
.NET Core X64 v4.1.1.0 | Microsoft.AspNetCore.Hosting version 1.1.0-rtm-22752 | Microsoft Windows 10.0.14393 | Need help? Yes please. :)

What did I miss?

@VahidN
Copy link

VahidN commented Jan 18, 2017

When you are customizing ASP.NET Core Identity, you should not use AddEntityFrameworkStores anymore. Because it will override all of your previous settings and customization to default Identity services.
First you need to create new services with the following signatures:

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>

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();

@HaoK
Copy link
Member

HaoK commented Jan 18, 2017

In 2.0 AddEntityFrameworkStores was improved to make things easier, the TKey argument was removed and the DbContext is sufficient to 'do the right thing'. You don't need to actually register the ApplicationXyz services unless you are consuming those services in your own code.

But AddUser/RoleStore is needed at a minimum for this to work.

@MaklaCof
Copy link
Author

Thank you very much.

@master-still
Copy link

@HaoK
Can you please provide guidance ?

identity_workaround

I Must be missing something for my UserStore !

@VahidN
Copy link

VahidN commented Jan 21, 2017

@master-still
It's fixed in v1.1: #988

@master-still
Copy link

master-still commented Jan 21, 2017

@VahidN Thank you

I am not sure what is wrong !

nugetrefrences

https://github.com/master-still/test

@MaklaCof
Copy link
Author

If it helps, this is my UserStore:

    public class AppUserStore : UserStore<AppUser, AppRole, AppDbContext, int, AppUserClaim, AppUserRole, AppUserLogin, AppUserToken, AppRoleClaim>
    {
        public AppUserStore(AppDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
        {
        }

        protected override AppUserClaim CreateUserClaim(AppUser user, Claim claim)
        {
            return new AppUserClaim(user, claim);
        }

        protected override AppUserLogin CreateUserLogin(AppUser user, UserLoginInfo login)
        {
            return new AppUserLogin(user, login);
        }

        protected override AppUserRole CreateUserRole(AppUser user, AppRole role)
        {
            return new AppUserRole(user, role);
        }

        protected override AppUserToken CreateUserToken(AppUser user, string loginProvider, string name, string value)
        {
            return new AppUserToken(user, loginProvider, name, value);
        }
    }

And this is my AppRole:

    public partial class AppRole : IdentityRole<int, AppUserRole, AppRoleClaim>
    {
    }

@master-still
Copy link

@MaklaCof https://github.com/master-still/test

currently I am having problem while passing my AppRole to Userstore.

@master-still
Copy link

Solved It ! Thank you all.

@mhannaford-oncore
Copy link

@master-still What did you do to fix this? I am using 1.1 and see the same error you reported. My code looks almost exactly the same as yours. Thanks

@mkontula
Copy link

mkontula commented May 3, 2017

Having the same issue. @master-still care to update your test repo with functioning code? Current version gives quite a bunch of build errors.

@Cubelaster
Copy link

Cubelaster commented Nov 7, 2018

Had the same problem. My problem started when I have overriden the IdentityUser to
public class User : IdentityUser<Guid>
This prompted the EF to not be able to build and show some wild errors. After finally figuring out that my DbContext needs to be in form of:
public class NgSchoolsContext : IdentityDbContext<User, IdentityRole<Guid>, Guid>
(notice the added IdentityRole and Guid: why is it necessary that we explicitly say that IdentityRole is also a Guid? Shouldn't it derive from User as it's base entity? )
the Add-Migration wouldn't work and i got a "No parameterless constructor" error. That part was fixed by adding
services.AddIdentityCore<User>() .AddEntityFrameworkStores<NgSchoolsContext>() .AddRoles<IdentityRole<Guid>>() .AddDefaultTokenProviders();
new Roles definition(?) in Startup. Also, why wouldn't it derive it's Id Type from it's IdentityUser implementation?
After that the solution could be built again. However!! Add-Migration was working, but update scripts couldn't execute because the scripts tried to change all PK columns to another type (from string to Guid for me) and that is a no go.
So after that, I dropped the Db and deleted all my migrations and just created a new initial migration and ran Update-Database. That solved it.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants