Reputation: 4661
I cannot find a way to properly extend ASP.NET Core's IdentityUser
(after extending: ApplicationUser
). I can override it and link it from other models, but cannot link from the user to other models. Simple data like CustomTag
works.
Source code of the issue: https://github.com/chanibal/CoreIdentityIssue
Or, to be specific, it's second commit that contains all the changes over the default template
What I did:
Changed the model overriding the IdentityUser
(as in official docs):
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{}
public DbSet<Bar> Bars { get; set; }
public DbSet<Foo> Foos { get; set; }
public DbSet<ApplicationUser> ApplicationUsers { get; set; } //< doesn't help
}
public class ApplicationUser : IdentityUser
{
public string CustomTag { get; set; } //< this works
public Bar Bar { get; set; } //< THIS DOES NOT WORK
}
public class Bar
{
public int Id { get; set; }
public int Value { get; set; }
}
public class Foo
{
public int Id { get; set; }
public ApplicationUser User { get; set; } //< this works
}
And migrated the changes
I'm updating the Bar values this way:
/// This should ensure a Bar is connected to a ApplicationUser
/// and increment it's value
[Authorize]
public async Task<IActionResult> IncrementBar()
{
// This DOES NOT work
var user = await _userManager.GetUserAsync(HttpContext.User);
if (user.Bar == null) //< this is always null
{
user.Bar = new Bar() { Value = 0 };
// _context.Add(user.Bar); //< doesn't help
}
user.Bar.Value++;
// await _userManager.UpdateAsync(user); //<> doesn't help
// await _signInManager.RefreshSignInAsync(user); //< doesn't help, starting to get desperate
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
The information is there in the database, accessible by SQL.
But somehow it doesn't hydrate the ApplicationUser
model:
Using Visual Studio 16.3.9
Upvotes: 4
Views: 190
Reputation: 239440
EF does not ever automatically load related entities. You must either eagerly or explicitly load the relationships. Eager loading is the preferred way, as it does joins to get all the data in a single query. However, UserManager<TUser>
provides no way to eagerly load relationships. As such, you have two choices:
Explicitly load the relationship. This will require an extra query, though.
var user = await _userManager.GetUserAsync(HttpContext.User);
await _context.Entry(user).Reference(x => x.Bar).LoadAsync();
// note: for collection props, you'd use `Collection(x => x.CollectionProp).LoadAsync()` instead.
Query the user from the context by the user id, instead of using UserManager<TUser>
:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _context.Users.Include(x => x.Bar).SingleOrDefaultAsync(x => x.Id == userId);
Upvotes: 2