EF Core 2如何在IdentityUser上包含角色导航属性?

时间:2017-12-12 07:26:08

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

我正在使用EF Core 2设置一个新项目,我需要在IdentityUser上有一个导航属性,所以当我查询用户时,我可以包含(x => x.Roles)并获取角色用户在。

这篇关于Github的帖子有一些想法,但我通过在Identity表上创建新的/重复的字段或导致迁移问题,尝试了每个问题和所有问题。 EF团队的任何人都没有官方评论。

https://github.com/aspnet/Identity/issues/1361

我想知道是否有人这样正常工作?并且可以共享他们的EF DB映射和模型。

3 个答案:

答案 0 :(得分:14)

请参阅“将身份验证和身份迁移到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 Migrations时防止重复的外键,请添加   以下内容适用于IdentityDbContext课程&#39; 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链接表访问用户对角色所持有的角色的任务。要通过导航属性访问角色实体本身,您需要添加另一个导航属性(这次是针对从IdentityUserRole继承的实体)。请参阅以下步骤:

  1. 修改Roles实体上的IdentityUser导航属性,如下所示:
  2. public virtual ICollection<UserRole> Roles { get; set; } = new List<UserRole>();
    
    1. 创建上面引用的UserRole实体:
    2. public class UserRole : IdentityUserRole<int>
      {
          public virtual IdentityRole<int> Role { get; set; }
      }
      
      1. 构建UserRole的映射,如下所示:
      2. builder.Entity<UserRole>()
            .HasOne(e => e.Role)
            .WithMany()
            .HasForeignKey(e => e.RoleId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Cascade);
        
        1. 然后,您可以按如下方式检索实体(填充了导航属性):
        2. 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();
          

答案 1 :(得分: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();

答案 2 :(得分: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参数。花了我几个小时来解决这个问题,希望它可以节省一些时间。