.net core .NET核心2.1标识:为每个角色创建一个表;桥M:M表

.net core .NET核心2.1标识:为每个角色创建一个表;桥M:M表,.net-core,entity-framework-core,asp.net-core-identity,.net Core,Entity Framework Core,Asp.net Core Identity,对于在.NET Core 2.1项目中使用Identity的基于角色的授权,我在找出适合我需要的最佳设计时遇到了问题 我已经用ApplicationUser类从Identity扩展了User类。 我需要5个不同的角色来控制对应用程序不同功能的访问: 管理员、教师、学生、家长和主管 所有公共属性都保存在User和ApplicationUser中,但我仍然需要与其他表的不同关系,这取决于用户的角色 角色教师中的用户链接到1-N学校 学生角色中的用户链接到1-N组学生(但不直接链接到学校) 角色“家

对于在.NET Core 2.1项目中使用Identity的基于角色的授权,我在找出适合我需要的最佳设计时遇到了问题

我已经用ApplicationUser类从Identity扩展了User类。 我需要5个不同的角色来控制对应用程序不同功能的访问:

管理员、教师、学生、家长和主管

所有公共属性都保存在User和ApplicationUser中,但我仍然需要与其他表的不同关系,这取决于用户的角色

  • 角色教师中的用户链接到1-N学校
  • 学生角色中的用户链接到1-N组学生(但不直接链接到学校)
  • 角色“家长”中的用户链接到1-N学生(但不链接到学校)
另一个要求是用户必须能够担任1-N角色

在我的情况下,最佳做法是什么

在身份的特征中,我有没有遗漏什么

起初我的想法是使用可为null的FK,但随着角色数量的增加,为所有这些记录设置这么多空字段似乎不是一个好主意

我在考虑使用“桥接表”将用户链接到每个角色的其他表。 ApplicationUser和桥接表之间存在多对多关系,桥接表和每个角色的各个表之间存在0-1关系。但这也没有真正的帮助,因为每个记录都会产生相同数量的空字段

我对.NETCore非常陌生,尤其是Identity,我可能缺少一些关键字来进行有效的研究,因为在我看来这是一个非常基本的系统(需求中没有什么特别的)

谢谢你的阅读

编辑: 我现在并没有真正的错误,因为我正在尝试在深入项目之前找出最佳实践。因为这是我第一次面对这样的需求,所以我试图找到关于优缺点的文档

我遵循马可的想法,将继承作为我的角色模型,因为这是我的第一个想法。我希望这将有助于理解我的担忧

public class ApplicationUser : IdentityUser
{
    public string CustomTag { get; set; }
    public string CustomTagBis { get; set; }
}
    public class Teacher : ApplicationUser
{
    public string TeacherIdentificationNumber { get; set; }
    public ICollection<Course> Courses { get; set; }
}
public class Student : ApplicationUser
{
    public ICollection<StudentGroup> Groups { get; set; }
}
public class Parent : ApplicationUser
{
    public ICollection<Student> Children { get; set; }
}
public class Course
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Category { get; set; }
}
public class StudentGroup
{
    public int Id { get; set; }
    public string Name { get; set; }
}
导致产生此代码:

protected override void OnModelCreating(ModelBuilder builder)
{
        //base.OnModelCreating(builder);
        builder.Entity<Student>().ToTable("Student");
        builder.Entity<Parent>().ToTable("Parent");
        builder.Entity<Teacher>().ToTable("Teacher");
}
模型创建时受保护的覆盖无效(ModelBuilder)
{
//基于模型创建(生成器);
builder.Entity().ToTable(“学生”);
builder.Entity().ToTable(“父项”);
builder.Entity().ToTable(“教师”);
}
我相信这些标识键映射在base.OneModelCreating中。 但即使我取消注释该行,我也会在数据库中保留相同的结果

经过一些研究,我发现这有助于我完成创建每类型表模型的过程并应用迁移

使用这种方法,我有一个如下的模式: 表1每种类型的方法

如果我错了,请纠正我,但这两种技术都符合我的要求,而且更多的是关于设计的偏好?它对体系结构和身份特征都没有重大影响


对于第三种选择,我想用一种不同的方法,但我不太确定

这样的设计符合我的要求吗?它有效吗? 我的意思是,把一个教师实体链接到一个角色而不是一个用户,感觉很奇怪。但在某种程度上,教师实体代表了用户在担任教师角色时所需的功能

对实体的作用


我还不太确定如何用EF-core实现这一点,以及重写IdentityRole类将如何影响Identity特性。我正在研究,但还没有弄明白。你所做的完全符合你的要求。您当前实现的称为“每个层次的表”。这是实体框架在发现其模型时的默认方法

另一种方法是每种类型的
表格
。在这种情况下,实体框架将创建4个表

  • 用户表
  • 学生桌
  • 教师桌
  • 父表
  • 由于所有这些实体都继承自
    ApplicationUser
    ,因此数据库将在它们与父类之间生成FK关系

    要实现这一点,您需要修改DbContext:

    public class FooContext : DbContext
    {
        public DbSet<ApplicationUser> Users { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Student>().ToTable("Students");
            modelBuilder.Entity<Parent>().ToTable("Parents");
            modelBuilder.Entity<Teacher>().ToTable("Teachers");
        }
    }
    
    公共类FooContext:DbContext
    {
    公共数据库集用户{get;set;}
    模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
    {
    modelBuilder.Entity().ToTable(“学生”);
    modelBuilder.Entity().ToTable(“父项”);
    modelBuilder.Entity().ToTable(“教师”);
    }
    }
    

    这应该是最规范化的方法。然而,还有第三种方法,您将得到3个表,并且父ApplicationUser类将映射到其具体实现中。但是,我从未用Asp.Net Identity实现过这一点,因此我不知道它是否会工作,也不知道您是否会遇到一些关键冲突。

    我建议您利用Asp.Net core的新功能和新的Identity framework。关于这个问题有很多文档

    您可以使用安全性,但在您的情况下,安全性似乎更合适

    最好的方法是不要混合上下文。将关注点分开:身份上下文(使用UserManager)和业务上下文(学校、您的DbContext)

    因为将ApplicationUser表放在“业务上下文”中意味着直接访问标识上下文。这不是使用标识的方式。使用UserManager进行与IdentityUser相关的查询

    为了使其正常工作,请在学校上下文中创建用户表,而不是继承ApplicationUser表。它不是一个副本,而是一个新表。事实上,唯一的共同点是UserId字段

    查看我的答案,了解关于更详细设计的想法

    移动场
    public class FooContext : DbContext
    {
        public DbSet<ApplicationUser> Users { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Student>().ToTable("Students");
            modelBuilder.Entity<Parent>().ToTable("Parents");
            modelBuilder.Entity<Teacher>().ToTable("Teachers");
        }
    }
    
    new Claim("http://school1.myapp.com/TeacherIdentificationNumber", 123);
    
    new Claim("http://school1.myapp.com/role", "Teacher");
    new Claim("http://school2.myapp.com/role", "Student");