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

Mou*_*ark 24 c# entity-framework-core asp.net-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; }
}
Run Code Online (Sandbox Code Playgroud)

SO *_*ood 22

不幸的是,您必须手动执行此操作,或者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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,为此实施整个商店并不是最好的选择.

您也可以直接查询商店:

UserManager<IdentityUser> userManager; // DI injected

var user = await userManager.Users
    .Include(x => x.Address)
    .SingleAsync(x => x.NormalizedEmail == email);
Run Code Online (Sandbox Code Playgroud)


小智 12

使用 EF Core 6.0 更新 .NET 6.0:

您现在可以将该属性配置为自动包含在每个查询中。

modelBuilder.Entity<MyUser>().Navigation(e => e.Address).AutoInclude();
Run Code Online (Sandbox Code Playgroud)

欲了解更多信息,请查看: https://learn.microsoft.com/en-us/ef/core/querying/lated-data/eager#model-configuration-for-auto-include-navigations


Chr*_*att 10

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

  1. 稍后明确加载关系:

    await context.Entry(user).Reference(x => x.Address).LoadAsync();
    
    Run Code Online (Sandbox Code Playgroud)

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

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

    var user = context.Users.Include(x => x.Address).SingleOrDefaultAsync(x=> x.Id == User.Identity.GetUserId());
    
    Run Code Online (Sandbox Code Playgroud)

FWIW,您virtual的导航属性不需要.这是懒惰加载,EF Core目前不支持.(虽然,EF Core 2.1,目前正在预览中,实际上会支持延迟加载.)无论如何,延迟加载通常是一个坏主意,所以你仍然应该坚持或明确地加载你的关系.


小智 5

我发现在 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());
}
Run Code Online (Sandbox Code Playgroud)