如何使用 UserManager 在 IdentityUser 上加载导航属性
How to load navigation properties on an IdentityUser with 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; }
}
不幸的是,您必须手动执行或创建自己的 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);
简短的回答:你不能。但是,有选项:
稍后显式加载关系:
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 实际上将支持延迟加载。)无论如何,延迟加载通常不是一个好主意,因此您仍然应该坚持急切或显式加载您的关系。
我发现在 UserManager 上写一个扩展很有用 class。
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());
}
带有 EF Core 6.0 的 .NET 6.0 更新:
您现在可以将 属性 配置为自动包含在每个查询中。
modelBuilder.Entity<MyUser>().Navigation(e => e.Address).AutoInclude();
在我的情况下,最好的选择是添加对 Microsoft.EntityFrameworkCore.Proxies 的包引用,然后在您的服务中使用 UseLazyLoadingProxies
.AddDbContext<YourDbContext>(
b => b.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString));
更多信息
https://docs.microsoft.com/de-de/ef/core/querying/related-data/lazy
我已经扩展 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; }
}
不幸的是,您必须手动执行或创建自己的 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);
简短的回答:你不能。但是,有选项:
稍后显式加载关系:
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 实际上将支持延迟加载。)无论如何,延迟加载通常不是一个好主意,因此您仍然应该坚持急切或显式加载您的关系。
我发现在 UserManager 上写一个扩展很有用 class。
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());
}
带有 EF Core 6.0 的 .NET 6.0 更新:
您现在可以将 属性 配置为自动包含在每个查询中。
modelBuilder.Entity<MyUser>().Navigation(e => e.Address).AutoInclude();
在我的情况下,最好的选择是添加对 Microsoft.EntityFrameworkCore.Proxies 的包引用,然后在您的服务中使用 UseLazyLoadingProxies
.AddDbContext<YourDbContext>(
b => b.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString));
更多信息 https://docs.microsoft.com/de-de/ef/core/querying/related-data/lazy