У меня проблемы с определением наилучшего дизайна, который соответствует моим потребностям в отношении авторизации на основе ролей с использованием Identity в проекте .NET Core 2.1.
Я уже расширил класс User с Identity с помощью ApplicationUserучебный класс.Мне нужно 5 разных ролей для управления доступом к различным функциям приложения:
Администратор, Учитель, Студент, Родитель и Супервизор
Все общие атрибуты хранятся в User и ApplicationUser, но япо-прежнему требуются разные отношения с другими таблицами в зависимости от роли пользователя.
- Пользователь в роли Учитель связан с 1-N School
- Пользователь в роли Ученик связан с 1-N GroupOfStudents(но не непосредственно в школу)
- Пользователь с ролью Родитель связан с 1-N учеником (но не со школой)
- ...
Другое требование заключается в том, что пользователь должен быть в состоянии роли 1-N.
Что было бы лучшим в моем случае?
Есть ли что-то, чего мне не хватает в функцияхof Identity?
Сначала я хотел использовать обнуляемую FK, но с увеличением количества ролей не похоже на хорошую идею иметь так много пустых полей для всех этих записей.
Я думал об использовании "бридтаблица ge ", чтобы связать пользователя с другими таблицами для каждой роли.Имейте отношение многие-ко-многим между ApplicationUser и таблицей мостов и отношения 0-1 между таблицей мостов и отдельными таблицами для каждой роли.Но это тоже не очень помогает, поскольку каждая запись будет содержать одинаковое количество пустых полей.
Я довольно новичок в .NET Core и особенно в 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; }
}
Это создаст базу данных, имеющую одну большую таблицу для Пользователя, содержащую все атрибуты:
Сгенерированная таблица пользователей
Я могу использовать это, и это будет работать.Пользователь может заполнить любое из этих пустых полей, если ему требуется другая роль.
Меня беспокоит то, что для каждой записи у меня будет огромное количество «неподходящих полей», которые останутся пустыми.Допустим, что на 1000 пользователей 80% пользователей являются студентами.Каковы последствия наличия 800 строк, содержащих: - пустой ParentId FK - пустой номер TeacherIdentificationNumber
И это лишь небольшая часть содержимого моделей.Он не «чувствует» себя правильно, я не прав?
Нет ли лучшего способа спроектировать сущности так, чтобы таблица User содержала только общие атрибуты для всех пользователей (как и должно быть?) и все еще сможет связать каждого пользователя с другой таблицей, которая свяжет пользователя с 1-N таблицами Teacher / Student / Parent / ... table?
Схема подхода Table-Per-Hierarchy
РЕДАКТИРОВАТЬ 2: Используя ответ Марко, я попытался использовать подход Table-Per-Type.При изменении моего контекста для реализации подхода Table-Per-Type я столкнулся с этой ошибкой, когда хотел добавить миграцию:
«Тип сущности IdentityUserLogin требует определения первичного ключа.»
Я полагаю, что это происходит, потому что я удалил:
base.OnModelCreating(builder);
В результате получился следующий код:
protected override void OnModelCreating(ModelBuilder builder)
{
//base.OnModelCreating(builder);
builder.Entity<Student>().ToTable("Student");
builder.Entity<Parent>().ToTable("Parent");
builder.Entity<Teacher>().ToTable("Teacher");
}
Я считаю, что эти ключи идентификации сопоставлены в base.OneModelCreating.Но даже если я раскомментирую эту строку, я сохраню тот же результат в своей базе данных.
После некоторого исследования я нашел эту статью , которая помогла мне пройти через процесс создания моделей таблиц для каждого типа и применить миграцию.
Используя этот подход, у меня естьСхема, которая выглядит следующим образом: Подход таблицы к типу
Исправьте меня, если я ошибаюсь, но обе методики соответствуют моим требованиям, и это больше о предпочтениях дизайна?Это не имеет большого значения ни в архитектуре, ни в особенностях идентификации?
Что касается третьего варианта, я подумывал использовать другой подход, но я не слишком уверен в этом.
Может ли подобный дизайн соответствовать моим требованиям и действителен ли он? Под действительным, я имею в виду, кажется странным связывать сущность учителя с ролью, а не с пользователем.Но в некотором смысле сущность учителя представляет функции, которые потребуются пользователю в роли учителя.
Роль в сущностях
Я не являюсьно слишком уверен в том, как реализовать это с ядром EF и как переопределение класса IdentityRole повлияет на функции Identity.Я нахожусь на этом, но еще не понял это.