EF Core 2 如何在 IdentityUser 上包含角色导航属性? [英] EF Core 2 How To Include Roles Navigation Property On IdentityUser?
问题描述
我正在使用 EF Core 2 设置一个新项目,我需要在 IdentityUser 上有一个导航属性,因此当我查询用户时,我可以包含 (x => x.Roles) 并获取用户的角色在.
Github 上的这篇文章有一些想法,但我已经尝试了每一个都会导致问题,通过在身份表上创建新/重复字段或导致迁移问题.EF 团队中的任何人都没有官方评论.
https://github.com/aspnet/Identity/issues/1361
我想知道是否有人可以正常工作?并且可以分享他们的 EF DB 映射和模型.
请参阅将身份验证和身份迁移到 ASP.NET Core 2.0"的文档,特别是添加 IdentityUser POCO 导航属性':
<块引用>实体框架(EF)核心导航属性基础IdentityUser
POCO(Plain Old CLR Object)已被删除.如果你的1.x 项目使用了这些属性,手动将它们添加回 2.0 项目:
//////此用户所属角色的导航属性.///</总结>公共虚拟 ICollection>角色{获取;} = new List>();
<块引用>
为防止在运行 EF Core Migrations 时出现重复的外键,添加以下是您的 IdentityDbContext
类' OnModelCreating
方法(在 base.OnModelCreating();
调用之后):
protected override void OnModelCreating(ModelBuilder builder){base.OnModelCreating(builder);//自定义 ASP.NET Identity 模型并在需要时覆盖默认值.//例如,您可以重命名 ASP.NET Identity 表名称等.//在调用 base.OnModelCreating(builder); 后添加您的自定义;builder.Entity().HasMany(e => e.Roles).WithOne().HasForeignKey(e => e.UserId).是必须的().OnDelete(DeleteBehavior.Cascade);}
编辑
以上仅满足通过 IdentityUserRole
链接表访问针对用户持有的角色 ID 的任务.要通过导航属性访问角色实体本身,您需要添加另一个导航属性(这次针对从 IdentityUserRole
继承的实体).请参阅以下步骤:
- 修改
IdentityUser
实体上的Roles
导航属性,如下所示:
public virtual ICollection角色{获取;放;} = new List();
- 创建上面引用的
UserRole
实体:
public class UserRole : IdentityUserRole{公共虚拟 IdentityRole角色{得到;放;}}
- 为
UserRole
构建映射如下:
builder.Entity().HasOne(e => e.Role).和很多().HasForeignKey(e => e.RoleId).是必须的().OnDelete(DeleteBehavior.Cascade);
- 然后您可以检索实体(填充导航属性),如下所示:
User user = context.Set().Include(u => u.Roles).ThenInclude(r => r.Role).FirstOrDefault();
注意:
- 由于这是加载多对多关系的另一面,这可能导致对数据库的多次调用(请参阅N+1 问题).
- 当您创建一个继承自
IdentityUserRole
的新实体时,您需要迁移或重新创建数据库. - 如果您想将此导航属性与
UserManager
或RoleManager
你需要使用长格式的重载AddUserStore()
和AddRoleStore
在你的启动类中,例如
services.AddIdentity>().AddUserStore, SqlContext, int, IdentityUserClaim, UserRole, IdentityUserLogin, IdentityUserToken, IdentityRoleClaim>().AddRoleStore、SqlContext、int、UserRole、IdentityRoleClaim>>().AddDefaultTokenProviders();
I am setting up a new project using EF Core 2, and I need to have a navigation property on the IdentityUser so when I query for a user I can include(x => x.Roles) and get the Roles the user is in.
This post on Github has some ideas, but I have tried each one and all cause issues, by creating new/duplicate fields on the Identity tables or cause issues with migrations. And no official comment from anyone in the EF team.
https://github.com/aspnet/Identity/issues/1361
I was wondering if anyone has this working correctly? And could share their EF DB mappings and models.
See the documentation for 'Migrating Authentication and Identity to ASP.NET Core 2.0', specifically the section 'Add IdentityUser POCO Navigation Properties':
The Entity Framework (EF) Core navigation properties of the base
IdentityUser
POCO (Plain Old CLR Object) have been removed. If your 1.x project used these properties, manually add them back to the 2.0 project:
/// <summary>
/// Navigation property for the roles this user belongs to.
/// </summary>
public virtual ICollection<IdentityUserRole<int>> Roles { get; } = new List<IdentityUserRole<int>>();
To prevent duplicate foreign keys when running EF Core Migrations, add the following to your
IdentityDbContext
class'OnModelCreating
method (after thebase.OnModelCreating();
call):
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<ApplicationUser>()
.HasMany(e => e.Roles)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
}
Edit
The above will only satisfy the task of accessing the role Ids held against a user via the IdentityUserRole
link table. To access the role entity itself via a navigation property, you would need to add another navigation property (this time against an entity inheriting from IdentityUserRole
). See the steps below:
- Modify the
Roles
navigation property on yourIdentityUser
entity as follows:
public virtual ICollection<UserRole> Roles { get; set; } = new List<UserRole>();
- Create the
UserRole
entity referenced above:
public class UserRole : IdentityUserRole<int>
{
public virtual IdentityRole<int> Role { get; set; }
}
- Construct the mapping for
UserRole
as follows:
builder.Entity<UserRole>()
.HasOne(e => e.Role)
.WithMany()
.HasForeignKey(e => e.RoleId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
- You can then retrieve entities (with the navigation property populated) as follows:
User user = context.Set<User>()
.Include(u => u.Roles)
.ThenInclude(r => r.Role)
.FirstOrDefault();
Note:
- As this is loading another side of a many-to-many relatiohship, this may result in more than one call to the database (see N+1 problem).
- As you are creating a new entity that inherits from
IdentityUserRole
you will need to migrate or re-create the database. - If you want to use this navigation property with
UserManager
orRoleManager
you will need to use the long-form overload ofAddUserStore()
andAddRoleStore
in your startup class, e.g.
services.AddIdentity<User, IdentityRole<int>>()
.AddUserStore<UserStore<User, IdentityRole<int>, SqlContext, int, IdentityUserClaim<int>, UserRole, IdentityUserLogin<int>, IdentityUserToken<int>, IdentityRoleClaim<int>>>()
.AddRoleStore<RoleStore<IdentityRole<int>, SqlContext, int, UserRole, IdentityRoleClaim<int>>>()
.AddDefaultTokenProviders();
这篇关于EF Core 2 如何在 IdentityUser 上包含角色导航属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!