【问题标题】:How to load navigation properties on an IdentityUser with UserManager如何使用 UserManager 在 IdentityUser 上加载导航属性
【发布时间】:2018-02-05 13:19:44
【问题描述】:

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

【问题讨论】:

  • AFAIK 没有内置方法,您需要“手动”完成。至少我没有找到一个,当我遇到同样的问题时。如果真的有办法,很高兴得到纠正......
  • 附注:[ForeignKey(nameof(AddressId))] 不是必需的,因为约定优于配置。
  • @CamiloTerevinto:这不是必需的,真的。但是,我总是添加属性只是为了明确。我不喜欢依赖约定的“魔术”,它也让代码更加明显。
  • 同样,特别是对于可能不熟悉 EF Core 及其约定的人。
  • 确认!多年来一直把我的头撞在墙上。即使在 Core 3.1 中,这仍然无法正常工作。文档 似乎 表明这应该有效 - 请参阅 docs.microsoft.com/en-us/aspnet/core/security/authentication/… - 但它没有。

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


【解决方案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);

【讨论】:

  • 使用 SingleOrDefaultAsync 代替 SingleAsync
【解决方案2】:

简短的回答:你不能。但是,有一些选择:

  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());
    

FWIW,您的导航属性不需要virtual。这是 EF Core 当前不支持的延迟加载。 (不过,目前处于预览阶段的 EF Core 2.1 实际上将支持延迟加载。)无论如何,延迟加载通常不是一个好主意,因此您仍然应该坚持急切地或显式地加载您的关系。

【讨论】:

  • 感谢关于延迟加载的提示。这个项目起源于 EF6,所以从那里,在代码库和我的脑海里有很多宿醉!
【解决方案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());
}

【讨论】:

    【解决方案4】:

    使用 EF Core 6.0 更新 .NET 6.0:

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

    modelBuilder.Entity<MyUser>().Navigation(e => e.Address).AutoInclude();
    

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

    【讨论】:

      猜你喜欢
      • 2018-05-25
      • 1970-01-01
      • 2019-08-20
      • 1970-01-01
      • 2016-11-21
      • 1970-01-01
      • 1970-01-01
      • 2020-01-26
      相关资源
      最近更新 更多