J'ai étendu IdentityUser
pour inclure une propriété de navigation pour l'adresse de l'utilisateur, mais lorsque vous obtenez l'utilisateur avec UserManager.FindByEmailAsync
, La propriété de navigation n'est pas remplie. ASP.NET Identity Core a-t-il un moyen de remplir les propriétés de navigation comme Include()
d'Entity Framework, ou dois-je le faire manuellement?
J'ai configuré la propriété de navigation comme ceci:
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; }
}
Malheureusement, vous devez le faire manuellement ou créer votre propre IUserStore<IdentityUser>
où vous chargez les données associées dans la méthode FindByEmailAsync
:
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);
}
}
Bien sûr, l'implémentation de l'ensemble du magasin juste pour cela n'est pas la meilleure option.
Vous pouvez également interroger le magasin directement, cependant:
UserManager<IdentityUser> userManager; // DI injected
var user = await userManager.Users
.Include(x => x.Address)
.SingleAsync(x => x.NormalizedEmail == email);
La réponse courte: vous ne pouvez pas. Cependant, il existe des options:
Chargez explicitement la relation plus tard:
await context.Entry(user).Reference(x => x.Address).LoadAsync();
Cela nécessitera bien sûr une requête supplémentaire, mais vous pouvez continuer à extraire l'utilisateur via UserManager
.
Utilisez simplement le contexte. Vous n'avez pas avez pour utiliser UserManager
. Cela rend simplement certaines choses un peu plus simples. Vous pouvez toujours recourir à l'interrogation directement via le contexte:
var user = context.Users.Include(x => x.Address).SingleOrDefaultAsync(x=> x.Id == User.Identity.GetUserId());
FWIW, vous n'avez pas besoin de virtual
sur votre propriété de navigation. C'est pour le chargement différé, qu'EF Core ne prend pas en charge actuellement. (Cependant, EF Core 2.1, actuellement en préversion, prend en charge le chargement paresseux.) Quoi qu'il en soit, le chargement paresseux est une mauvaise idée le plus souvent, vous devez donc vous en tenir à charger vos relations avec impatience ou explicitement.
J'ai trouvé utile d'écrire une extension sur la classe UserManager.
public static async Task<MyUser> FindByUserAsync(
this UserManager<MyUser> input,
ClaimsPrincipal user )
{
return await input.Users
.Include(x => x.InverseNavigationTable)
.SingleOrDefaultAsync(x => x.NormalizedUserName == user.Identity.Name.ToUpper());
}