Matthew Layton
Matthew Layton

Reputation: 42380

AspNetCore.Identity not working with custom User/Role implementation

Because I tend to favour Guid as my primary key type, my User and Role classes are implemented as follows

public class User : IdentityUser<Guid, UserClaim, UserRole, UserLogin>
{
}

public class Role : IdentityRole<Guid, UserRole, RoleClaim>
{
}

Note that UserClaim, UserRole, UserLogin & RoleClaim are all implemented in the same manner

Here is my DbContext implementation

public class ApplicationDbContext : IdentityDbContext<User, Role, Guid, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
{
}

All good so far, except AspNetCore's new DI container doesn't seem to like my custom implementation by default. The following line of code, from my Startup.cs file throws the error shown below

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

GenericArguments[0], 'NewCo.DomainModel.Models.Identity.User', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[TUser,TRole,TContext,TKey]' violates the constraint of type 'TUser'.

I am assuming that this is because the default Identity gremlin is implemented to use IdentityUser<string>, where I'm using IdentityUser<Guid>.

What do I do next? (I'm all outta ideas)

Note: I'm building against Microsoft's official .NET Core and ASP.NET Core releases as of Monday (June 27th, 2016) and Visual Studio Update 3 (if that's of any help to anyone)

Upvotes: 4

Views: 9238

Answers (4)

Sagheer Khan
Sagheer Khan

Reputation: 31

specify Key to .AddEntityFrameworkStore() method in case of using custom User and Role entities.while registering identity in startup

.AddEntityFrameworkStores<IdentityDbContext, TKey>()

because it uses by default key type string and will cause error in case of other key type.

Upvotes: 0

Behnam Abdy
Behnam Abdy

Reputation: 385

I ended up with this solution:
Actually I made my own abstract IdentityDbContext then you can pass any custom model, the only issue is when creating the db EF adds one Disriminator field to all tables other than user and role(inheritance)

public abstract class ApplicationDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken> : IdentityDbContext<TUser, TRole, TKey>
    where TUser : IdentityUser<TKey>
    where TRole : IdentityRole<TKey>
    where TKey : IEquatable<TKey>
    where TUserClaim : IdentityUserClaim<TKey>
    where TUserRole : IdentityUserRole<TKey>
    where TUserLogin : IdentityUserLogin<TKey>
    where TRoleClaim : IdentityRoleClaim<TKey>
    where TUserToken : IdentityUserToken<TKey>
{
    public ApplicationDbContext(DbContextOptions options) : base(options) { }

    protected ApplicationDbContext() { }

    public new DbSet<TRoleClaim> RoleClaims { get; set; }
    public new DbSet<TRole> Roles { get; set; }
    public new DbSet<TUserClaim> UserClaims { get; set; }
    public new DbSet<TUserLogin> UserLogins { get; set; }
    public new DbSet<TUserRole> UserRoles { get; set; }
    public new DbSet<TUser> Users { get; set; }
    public new DbSet<TUserToken> UserTokens { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}

public class AppDbContext : ApplicationDbContext<ApplicationUser, ApplicationRole, Guid, ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin, ApplicationRoleClaim, ApplicationUserToken>
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }

    //public new DbSet<ApplicationUserClaim> UserClaims { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);
    }
}

  services.AddIdentity<ApplicationUser, ApplicationRole>()
                .AddEntityFrameworkStores<AppDbContext, Guid>()
                .AddDefaultTokenProviders()
                .AddUserStore<UserStore<ApplicationUser, ApplicationRole, AppDbContext, Guid>>()
                .AddRoleStore<RoleStore<ApplicationRole, AppDbContext, Guid>>()

public class ApplicationUser : IdentityUser<Guid>
{
    public ApplicationUser()
    {
        //this.Id = Guid.NewGuid();
    }

    public ApplicationUser(string userName) : this() { this.UserName = userName; }
    public Guid ClientId { get; set; }
    //public new ICollection<ApplicationUserClaim> Claims { get; set; }
}

//public class ApplicationRole : IdentityRole<Guid, ApplicationUserRole, ApplicationRoleClaim>
public class ApplicationRole : IdentityRole<Guid>
{
    public ApplicationRole()
    {
        //this.Id = Guid.NewGuid();
    }

    public ApplicationRole(string name) : this() { this.Name = name; }
}

public class ApplicationRoleClaim : IdentityRoleClaim<Guid> { }
//[NotMapped]
public class ApplicationUserClaim : IdentityUserClaim<Guid> { }

public class ApplicationUserLogin : IdentityUserLogin<Guid> { }

public class ApplicationUserRole : IdentityUserRole<Guid> { }

public class ApplicationUserToken : IdentityUserToken<Guid> { }

Upvotes: 5

Jerry
Jerry

Reputation: 1527

"UserStore`4[TUser,TRole,TContext,TKey]' violates the constraint of type 'TUser'."

You need to create a UserStore using the Guid, as well as change your DbContext

public class ApplicationUser : IdentityUser<Guid> { }

public class ApplicationRole : IdentityRole<Guid> { }

public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, ApplicationDbContext, Guid>
{
    public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
    {
    }
}

New ApplicationDbContext inherit

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}

In your startup.cs

services
    .AddIdentity<ApplicationUser, ApplicationRole>()
    .AddUserStore<ApplicationUserStore>()
    .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
    .AddDefaultTokenProviders();

Upvotes: 0

K&#233;vin Chalet
K&#233;vin Chalet

Reputation: 42110

Since you're using a custom key type, you must specify it when calling AddEntityFrameworkStores:

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

Upvotes: 6

Related Questions