EF Core 2 如何在 IdentityUser 上包含角色导航属性?
Posted
技术标签:
【中文标题】EF Core 2 如何在 IdentityUser 上包含角色导航属性?【英文标题】:EF Core 2 How To Include Roles Navigation Property On IdentityUser? 【发布时间】:2018-05-25 19:44:30 【问题描述】:我正在使用 EF Core 2 设置一个新项目,并且我需要在 IdentityUser 上有一个导航属性,因此当我查询用户时,我可以包含(x => x.Roles)并获取用户的角色在里面。
Github 上的这篇文章有一些想法,但我已经尝试了每一个和所有导致问题的方法,方法是在身份表上创建新的/重复的字段或导致迁移问题。 EF 团队中的任何人都没有发表官方评论。
https://github.com/aspnet/Identity/issues/1361
我想知道是否有人可以正常工作?并且可以分享他们的 EF DB 映射和模型。
【问题讨论】:
.Net Core 2.1 的最新修复解决了我的问题***.com/questions/51004516/… 【参考方案1】:将我的 10 美分加到已经很出色的 accepted answer 上。请务必记住使您的 DbContext
适应您将要进行的更改。
在接受答案的情况下,您 DbContext
应该看起来类似于:
public class DbContext: IdentityDbContext<User, IdentityRole<int>, string,
IdentityUserClaim<string>, UserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>,
IDesignTimeDbContextFactory<ApplicationContext>
// ...Rest of the code goes here
注意UserRole
替换了默认的TUserRole
参数。我花了几个小时才弄清楚这一点,我希望它可以节省其他人的时间。
【讨论】:
在添加迁移时使用此实现,您是否遇到过这样的错误? “实体类型 'IdentityUserLoginbuilder.Entity<IdentityUserLogin<string>>().HasKey(x => x.UserId);
的方法来覆盖OnModelCreating
方法。在这个答案中做了类似的事情 (***.com/a/45679795/8058709)
感谢您的建议,我已经设法以其他方式修复它。我找到了原因,在我的例子中,onModelCreating() 中有一些映射同时使用了 IdentityUserLogin 和 ApplicationUserLogin。似乎 EF 对选择其中一种模型感到困惑。我通过用我的自定义模型(ApplicationUserLogin)替换所有默认(IdentityUserLogin)模型来修复它。这实际上对我有用。【参考方案2】:
请参阅“将身份验证和身份迁移到 ASP.NET Core 2.0”的文档,特别是“Add IdentityUser POCO Navigation Properties”部分:
基础的实体框架 (EF) 核心导航属性
IdentityUser
POCO(普通旧 CLR 对象)已被删除。如果你的 1.x 项目使用了这些属性,手动将它们添加回 2.0 项目:
/// <summary>
/// Navigation property for the roles this user belongs to.
/// </summary>
public virtual ICollection<IdentityUserRole<int>> Roles get; = new List<IdentityUserRole<int>>();
为防止在运行 EF Core 迁移时出现重复的外键,请添加 以下是您的
IdentityDbContext
班级'OnModelCreating
方法(在base.OnModelCreating();
调用之后):
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);
编辑
以上内容仅满足通过IdentityUserRole
链接表访问对用户持有的角色ID 的任务。要通过导航属性访问角色实体本身,您需要添加另一个导航属性(这次针对从IdentityUserRole
继承的实体)。请参阅以下步骤:
-
修改
IdentityUser
实体上的Roles
导航属性,如下所示:
public virtual ICollection<UserRole> Roles get; set; = new List<UserRole>();
-
创建上面引用的
UserRole
实体:
public class UserRole : IdentityUserRole<int>
public virtual IdentityRole<int> Role get; set;
-
如下构造
UserRole
的映射:
builder.Entity<UserRole>()
.HasOne(e => e.Role)
.WithMany()
.HasForeignKey(e => e.RoleId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
-
然后您可以检索实体(填充导航属性),如下所示:
User user = context.Set<User>()
.Include(u => u.Roles)
.ThenInclude(r => r.Role)
.FirstOrDefault();
注意:
由于这是加载多对多关系的另一端,这可能会导致对数据库的多次调用(请参阅N+1 problem)。 当您创建一个继承自IdentityUserRole
的新实体时,您需要迁移或重新创建数据库。
如果您想将此导航属性与UserManager
或
RoleManager
您将需要使用长格式重载
AddUserStore()
和 AddRoleStore
在你的启动课程中,例如
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();
【讨论】:
感谢您抽出宝贵时间回复并感谢您提供的信息。这只是 UserRoles 而不是角色?不幸的是,如果没有另一个数据库查找来获取角色名称,我仍然没有办法? @leen3o:啊,我现在明白这个问题了。请参阅我的编辑。但是,请注意,它可能无法通过单个数据库调用来实现。 优秀。我希望我能给这个更多的赞成票。谢谢。 不客气 :)。它导致多个数据库调用的事实可能使这有点无用,但认为无论如何都值得添加。 @leen3o 另一个注意事项:由于这会创建一个继承自IdentityUserRole
的新实体,因此您需要迁移或重新创建数据库。【参考方案3】:
我通过自定义查询获取角色,可能会有所帮助。
var roles = (from role in _dbContext.Roles
let userRoles = _dbContext.UserRoles.Where(ur => ur.UserId == user.Id).Select(ur => ur.RoleId)
where userRoles.Contains(role.Id)
select role
).ToList();
【讨论】:
以上是关于EF Core 2 如何在 IdentityUser 上包含角色导航属性?的主要内容,如果未能解决你的问题,请参考以下文章