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

Error with custom Entities when calling UserManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey); #1084

Closed
MaklaCof opened this issue Jan 24, 2017 · 3 comments

Comments

@MaklaCof
Copy link

I am trying to add external login(Google) with OpenIddict. I am following tutorials online and from this book.
When I call var user = await UserManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey); I get error:

{"error":"Entity type 'AppUserLogin' is defined with a single key property, but 2 values were passed to the 'DbSet.Find' method."}

I override all Identity classes to use int for key and I have custom Stores defined. I followed answer in this thread:

  [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; }
    }

    public class AppRoleManager : RoleManager<AppRole>
    {
        public AppRoleManager(IRoleStore<AppRole> store, IEnumerable<IRoleValidator<AppRole>> roleValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, ILogger<RoleManager<AppRole>> logger, IHttpContextAccessor contextAccessor) : base(store, roleValidators, keyNormalizer, errors, logger, contextAccessor)
        {
        }
    }
    public class AppRoleStore : RoleStore<AppRole, AppDbContext, int, AppUserRole, AppRoleClaim>
    {
        public AppRoleStore(AppDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
        {
        }

        protected override AppRoleClaim CreateRoleClaim(AppRole role, Claim claim)
        {
            return new AppRoleClaim(role, claim);
        }
    }
    public class AppSignInManager : SignInManager<AppUser>
    {
        public AppSignInManager(UserManager<AppUser> userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<AppUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor, ILogger<SignInManager<AppUser>> logger) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger)
        {
        }
    }
    public class AppUserManager : UserManager<AppUser>
    {
        public AppUserManager(IUserStore<AppUser> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<AppUser> passwordHasher, IEnumerable<IUserValidator<AppUser>> userValidators, IEnumerable<IPasswordValidator<AppUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<AppUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
        {
            var LoginStore = Store;
        }
    }
    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);
        }
    }

When I tried to follow source code I think I need to override FindByLoginAsync method but I am not sure why, where or how the code should look (return query from Db something like:return this.Context.UserLogins.Where(u => { u.LoginProvider = loginProvider && u.ProviderKey = providerKey});)?
Can someone please point me into the right direction.

@MaklaCof
Copy link
Author

Solution is to change code in IdentityDbContext.OnModelCreating method.
From

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

to

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

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<AppUserLogin>(entity =>
            {
                entity
                .HasKey(u => u.Id);
                entity.Property(p => p.Id)
                .ValueGeneratedOnAdd();
                entity
                .HasKey(u => new { u.LoginProvider, u.ProviderKey });
            });

and it works.

@divega
Copy link

divega commented Jan 28, 2017

@MaklaCof just to clarify, you are configuring the key of the user login twice. The last configuration will win, so the one that configures Id as a key will be ignored. Hence the Id property you added to the entity serves no purpose.

@MaklaCof
Copy link
Author

Thanks.

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

2 participants