问题
I am trying to link my Identity user table to a user detail table I have created to track other user information. That user detail table is called UserProfile.
I came across this link but it is not working in .NET Core 2.1: Link ASP.NET Identity users to user detail table
Here is what I have currently:
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; }
}
However, elsewhere in the code I call:
var user = await _userMgr.FindByNameAsync(model.UserName);
And user.UserProfile is null.
I have tried many combinations of data annotations and even fluent api to no avail.
modelBuilder.Entity<ApplicationUser>()
.HasOne(c => c.UserProfile)
.WithOne(t => t.User)
.HasForeignKey<UserProfile>(b => b.UserId);
I also tried turning on lazy loading but that couldn't even load the property.
Does anyone know how to do this in .Net Core 2.1?
Thanks
回答1:
Your main issue is simply that UserManager
has no functionality to include related entities, such as your UserProfile
. As a result, you have two options:
Use your context directly instead. Then you can eagerly load your
UserProfile
along with theApplicationUser
instance, in just one query to the database:var user = await _context.Users.Include(x => x.UserProfile).SingleOrDefaultAsync(x => x.UserName == model.UserName);
You can explicitly load the related
UserProfile
instead. This will result in an extra query, though, for a total of two: one to get the user and one to get the related profile:await _context.Entry(user).Reference(x => x.UserProfile).LoadAsync();
However, frankly, you should not have UserProfile
at all. ASP.NET Identity is not like ASP.NET Membership. With the latter, you had to have a separate UserProfile
because the "user" in Membership was not extensible. In Identity, the user is extensible, so if you want additional profile information on it, just add it to the class:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; } // FirstName
public string LastName { get; set; } // LastName
}
Note that I trimmed a lot of cruft here as well. There's no point in overriding the Id
and then leaving it auto-implemented. Additionally, you obviously don't need the UserName
property from UserProfile
because IdentityUser
already has that, which then of course means your ApplicationUser
has it as well.
UPDATE
How the user data is persisted doesn't have to affect whether it can be a claim or not. In other words, you don't have to literally save data as claim in order to access it as claim. Just derive from UserClaimsPrincipalFactory<TUser>
, override CreateAsync
, and then register it with service collection as scoped.
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;
}
}
Then in ConfigureServices
:
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, MyClaimsPrincipalFactory>();
来源:https://stackoverflow.com/questions/51996859/link-asp-net-identity-table-to-a-user-detail-table