我正在尝试将我的标识用户表链接到我为跟踪其他用户信息而创建的用户详细信息表。该用户详细信息表称为用户配置文件。
我遇到了这个链接,但它在.NET Core 2.1中不起作用: ASP.NET 标识用户链接到用户详细信息表
这是我目前拥有的:
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; }
}
但是,在我调用的代码中的其他地方:
var user = await _userMgr.FindByNameAsync(model.UserName);
和用户。用户配置文件为空。
我已经尝试了许多数据注释甚至流畅的 API 组合,但无济于事。
modelBuilder.Entity<ApplicationUser>()
.HasOne(c => c.UserProfile)
.WithOne(t => t.User)
.HasForeignKey<UserProfile>(b => b.UserId);
我还尝试打开延迟加载,但这甚至无法加载属性。
有谁知道如何在.Net Core 2.1中做到这一点?
谢谢
您的主要问题只是UserManager
没有包含相关实体的功能,例如您的UserProfile
。因此,您有两种选择:
-
请直接使用上下文。然后,您可以在对数据库的一个查询中急切地加载
UserProfile
与ApplicationUser
实例:var user = await _context.Users.Include(x => x.UserProfile).SingleOrDefaultAsync(x => x.UserName == model.UserName);
-
您可以改为显式加载相关
UserProfile
。但是,这将导致一个额外的查询,总共有两个:一个用于获取用户,另一个用于获取相关配置文件:await _context.Entry(user).Reference(x => x.UserProfile).LoadAsync();
但是,坦率地说,您根本不应该拥有UserProfile
。 ASP.NET 身份不像 ASP.NET 会员资格。对于后者,您必须有一个单独的UserProfile
,因为成员资格中的"用户"是不可扩展的。在 Identity 中,用户是可扩展的,因此,如果需要有关它的其他配置文件信息,只需将其添加到类中:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; } // FirstName
public string LastName { get; set; } // LastName
}
请注意,我在这里也修剪了很多面包屑。覆盖Id
然后让它自动实现是没有意义的。此外,您显然不需要UserProfile
的UserName
属性,因为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;
}
}
然后在ConfigureServices
:
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, MyClaimsPrincipalFactory>();