如何使用UserManager在IdentityUser上加载导航属性

时间:2018-02-05 13:19:45

标签: c# asp.net-core entity-framework-core asp.net-core-identity

我已经扩展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; }
}

3 个答案:

答案 0 :(得分:16)

不幸的是,您必须手动执行此操作,或者创建自己的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);

答案 1 :(得分:10)

简短的回答:你做不到。但是,有选择:

  1. 稍后明确加载关系:

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

    这当然需要发出额外的查询,但您可以继续通过UserManager拉出用户。

  2. 只需使用上下文。您没有 使用UserManager。它只是使一些事情变得更简单。您可以随时通过上下文直接回溯查询:

    var user = context.Users.Include(x => x.Address).SingleOrDefaultAsync(x=> x.Id == User.Identity.GetUserId());
    
  3. FWIW,您的导航属性不需要virtual。这是懒惰加载,EF Core目前不支持。 (虽然,EF Core 2.1,目前正在预览中,实际上会支持延迟加载。)无论如何,延迟加载通常是一个坏主意,所以你仍然应该坚持或明确加载你的关系。

答案 2 :(得分:1)

我发现在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());
}