Как загрузить свойства навигации в IdentityUser с помощью UserManager
Я расширил IdentityUser
чтобы включить свойство навигации для адреса пользователя, однако при получении пользователя с UserManager.FindByEmailAsync
свойство навигации не заполняется. Является ли ASP.NET Identity Core каким-то образом заполнять свойства навигации, такие как Entity Framework Include()
, или мне нужно сделать это вручную?
Я создал свойство навигации следующим образом:
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; }
}
Ответы
Ответ 1
К сожалению, вам нужно либо сделать это вручную, либо создать свой собственный IUserStore<IdentityUser>
где вы загружаете связанные данные в метод 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);
}
}
Конечно, реализация всего магазина только для этого - не лучший вариант.
Вы также можете напрямую запросить магазин:
UserManager<IdentityUser> userManager; // DI injected
var user = await userManager.Users
.Include(x => x.Address)
.SingleAsync(x => x.NormalizedEmail == email);
Ответ 2
Короткий ответ: вы не можете. Однако есть варианты:
-
Явно загрузите отношение позже:
await context.Entry(user).Reference(x => x.Address).LoadAsync();
Разумеется, для этого потребуется выдача дополнительного запроса, но вы можете продолжать вытаскивать пользователя через UserManager
.
-
Просто используйте контекст. Вам не нужно использовать UserManager
. Это немного упрощает некоторые вещи. Вы всегда можете вернуться к запросу напрямую через контекст:
var user = context.Users.Include(x => x.Address).SingleOrDefaultAsync(x=> x.Id == User.Identity.GetUserId());
FWIW, вам не нужно virtual
свойство навигации. Это для ленивой загрузки, которую EF Core в настоящее время не поддерживает. (Хотя EF Core 2.1, в настоящее время в предварительном просмотре, фактически будет поддерживать ленивую загрузку.) Несмотря на это, ленивая загрузка - это плохая идея чаще, чем нет, поэтому вы все равно должны придерживаться либо с нетерпением, либо с явной загрузкой своих отношений.
Ответ 3
Я нашел полезным написать расширение для класса 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());
}