I have used the AspNetCore.Identity
in my Asp.Net Core application and I want to call the method called FindByLoginAsync
but result is always NULL.
Versions:
Microsoft.AspNetCore.Identity.EntityFrameworkCore (1.1.1)
Microsoft.AspNetCore.Identity (1.1.1)
Code:
var loginProvider = "Github"
var providerKey = "1234567";
var user = await _userManager.FindByLoginAsync(loginProvider, providerKey);
This record exists in the database, but this method returns always NULL.
I've tried trace the SQL query and I've got this:
exec sp_executesql N'SELECT TOP(1) [e].[ProviderKey], [e].[LoginProvider], [e].[ProviderDisplayName], [e].[UserId]
FROM [UserLogins] AS [e]
WHERE ([e].[ProviderKey] = @__get_Item_0) AND ([e].[LoginProvider] = @__get_Item_1)',N'@__get_Item_0 nvarchar(450),@__get_Item_1 nvarchar(450)',@__get_Item_0=N'Github',@__get_Item_1=N'1234567'
My SQL query is like [e].[LoginProvider]
the value providerKey
and [e].[ProviderKey]
the value loginProvider
.
Application DbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(i =>
{
i.ToTable("Users");
i.HasKey(x => x.Id);
});
builder.Entity<ApplicationRole>(i =>
{
i.ToTable("Roles");
i.HasKey(x => x.Id);
});
builder.Entity<IdentityUserRole<int>>(i =>
{
i.ToTable("UserRoles");
i.HasKey(x => new { x.RoleId, x.UserId });
});
builder.Entity<IdentityUserLogin<int>>(i =>
{
i.ToTable("UserLogins");
i.HasKey(x => new { x.ProviderKey, x.LoginProvider });
});
builder.Entity<IdentityRoleClaim<int>>(i =>
{
i.ToTable("RoleClaims");
i.HasKey(x => x.Id);
});
builder.Entity<IdentityUserClaim<int>>(i =>
{
i.ToTable("UserClaims");
i.HasKey(x => x.Id);
});
builder.Entity<IdentityUserToken<int>>(i =>
{
i.ToTable("UserTokens");
i.HasKey(x => x.UserId);
});
}
}
Implementation of IdentityUser, IdentityRole
public class ApplicationUser : IdentityUser<int>
{
}
public class ApplicationRole : IdentityRole<int>
{
}
How can I fix this? How is this behaviour possible?
You have incorrect order of primary keys in registration of entity IdentityUserLogin
. Change it to this
builder.Entity<IdentityUserLogin<int>>(i =>
{
i.ToTable("UserLogins");
i.HasKey(x => new { x.LoginProvider, x.ProviderKey });
});
That's the fix, now the rationale behind.
In version 1.1.1 the method UserStore.FindByLoginAsync
used method DbSet.FindAsync
, which accepts ordered array of values for primary keys. The order must follow the order used in entity registration.
public async virtual Task<TUser> FindByLoginAsync(string loginProvider, string providerKey,
CancellationToken cancellationToken = default(CancellationToken))
{
...
var userLogin = await UserLogins.FindAsync(new object[] { loginProvider, providerKey }, cancellationToken);
...
}
In the default implementation the primary keys are registered in correct order
builder.Entity<TUserLogin>(b =>
{
b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
b.ToTable("AspNetUserLogins");
});