Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load navigation properties on an IdentityUser with UserManager

I've extended IdentityUser to include a navigation property for the user's address, however when getting the user with UserManager.FindByEmailAsync, the navigation property isn't populated. Does ASP.NET Identity Core have some way to populate navigation properties like Entity Framework's Include(), or do I have to do it manually?

I've set up the navigation property like this:

public class MyUser : IdentityUser {     public int? AddressId { get; set; }      [ForeignKey(nameof(AddressId))]     public virtual Address Address { get; set; } }  public class Address {     [Key]     public int Id { get; set; }     public string Street { get; set; }     public string Town { get; set; }     public string Country { get; set; } } 
like image 591
Mourndark Avatar asked Feb 05 '18 13:02

Mourndark


People also ask

What is Identity. net Core?

ASP.NET Core Identity: Is an API that supports user interface (UI) login functionality. Manages users, passwords, profile data, roles, claims, tokens, email confirmation, and more.


2 Answers

Unfortunately, you have to either do it manually or create your own IUserStore<IdentityUser> where you load related data in the FindByEmailAsync method:

public class MyStore : IUserStore<IdentityUser>, // the rest of the interfaces {     // ... implement the dozens of methods     public async Task<IdentityUser> FindByEmailAsync(string normalizedEmail, CancellationToken token)     {         return await context.Users             .Include(x => x.Address)             .SingleAsync(x => x.Email == normalizedEmail);     } } 

Of course, implementing the entire store just for this isn't the best option.

You can also query the store directly, though:

UserManager<IdentityUser> userManager; // DI injected  var user = await userManager.Users     .Include(x => x.Address)     .SingleAsync(x => x.NormalizedEmail == email); 
like image 199
Camilo Terevinto Avatar answered Oct 18 '22 01:10

Camilo Terevinto


The short answer: you can't. However, there's options:

  1. Explicitly load the relation later:

    await context.Entry(user).Reference(x => x.Address).LoadAsync(); 

    This will require issuing an additional query of course, but you can continue to pull the user via UserManager.

  2. Just use the context. You don't have to use UserManager. It just makes some things a little simpler. You can always fallback to querying directly via the context:

    var user = context.Users.Include(x => x.Address).SingleOrDefaultAsync(x=> x.Id == User.Identity.GetUserId()); 

FWIW, you don't need virtual on your navigation property. That's for lazy-loading, which EF Core currently does not support. (Though, EF Core 2.1, currently in preview, will actually support lazy-loading.) Regardless, lazy-loading is a bad idea more often than not, so you should still stick to either eagerly or explicitly loading your relationships.

like image 20
Chris Pratt Avatar answered Oct 18 '22 01:10

Chris Pratt