将 ASP.Net Identity 表链接到用户详细信息表

The*_*bbs 4 .net asp.net-identity entity-framework-core asp.net-core asp.net-core-2.1

我正在尝试将我的 Identity 用户表链接到我创建的用于跟踪其他用户信息的用户详细信息表。该用户详细信息表称为 UserProfile。

我遇到了这个链接,但它在 .NET Core 2.1 中不起作用: Link ASP.NET Identity users to user detail table

这是我目前所拥有的:

public class ApplicationUser : IdentityUser
{

    [Key]
    public override string Id { get; set; }

    [ForeignKey("Id")]
    public virtual UserProfile UserProfile { get; set; }

}

[Table("UserProfile")]
public class UserProfile
{
    [Key, ForeignKey("User")]
    public string UserId { get; set; } // UserId (Primary key) 
    public string UserName { get; set; } // UserName 
    public string FirstName { get; set; } // FirstName
    public string LastName { get; set; } // LastName

    //[ForeignKey("Id")]
    public virtual ApplicationUser User { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

但是,在我调用的代码中的其他地方:

var user = await _userMgr.FindByNameAsync(model.UserName);
Run Code Online (Sandbox Code Playgroud)

并且 user.UserProfile 为空。

我尝试了很多数据注释的组合,甚至连流畅的api都无济于事。

   modelBuilder.Entity<ApplicationUser>()
       .HasOne(c => c.UserProfile)
       .WithOne(t => t.User)
       .HasForeignKey<UserProfile>(b => b.UserId);
Run Code Online (Sandbox Code Playgroud)

我也尝试打开延迟加载,但这甚至无法加载该属性。

有谁知道如何在 .Net Core 2.1 中做到这一点?

谢谢

Chr*_*att 5

您的主要问题只是UserManager没有包含相关实体的功能,例如您的UserProfile. 因此,您有两个选择:

  1. 直接使用您的上下文。然后,您可以急切地加载您UserProfileApplicationUser实例,只需对数据库进行一次查询:

    var user = await _context.Users.Include(x => x.UserProfile).SingleOrDefaultAsync(x => x.UserName ==  model.UserName);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 您可以显式加载相关内容UserProfile。但是,这将导致额外的查询,总共有两个:一个获取用户,一个获取相关配置文件:

    await _context.Entry(user).Reference(x => x.UserProfile).LoadAsync();
    
    Run Code Online (Sandbox Code Playgroud)

但是,坦率地说,您根本不应该拥有UserProfile。ASP.NET Identity 与 ASP.NET Membership 不同。对于后者,您必须有一个单独的,UserProfile因为 Membership 中的“用户”是不可扩展的。在 Identity 中,用户可扩展的,因此如果您想要关于它的其他配置文件信息,只需将其添加到类中:

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; } // FirstName
    public string LastName { get; set; } // LastName
}
Run Code Online (Sandbox Code Playgroud)

请注意,我也在这里修剪了很多杂物。覆盖Id然后让它自动实现是没有意义的。此外,您显然不需要该UserName属性,UserProfile因为IdentityUser已经拥有该属性,这当然意味着您ApplicationUser也拥有它。

更新

用户数据的持久化方式不一定会影响它是否可以声明。换句话说,您不必将数据逐字保存为声明,以便将其作为声明访问。只需派生自UserClaimsPrincipalFactory<TUser>、覆盖CreateAsync,然后将其注册到服务集合作为范围。

public class MyClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
{
    public MyClaimsPrincipalFactory(UserManager<TUser> userManager, IOptions<IdentityOptions> optionsAccessor)
        : base(userManager, optionsAccessor)
    {
    }

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);
        ((ClaimsIdentity)principal.Identity).AddClaims(new[]
        {
            new Claim(ClaimTypes.GivenName, user.FirstName),
            new Claim(ClaimTypes.Surname, user.LastName),
            // etc.
        });

        return principal;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在ConfigureServices

services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, MyClaimsPrincipalFactory>();
Run Code Online (Sandbox Code Playgroud)