Как получить список учебных предметов для студента с помощью LINQ to SQL?

Есть, примерно, такая схема на C# и EntityFramework Core (v. 5.0.11):

// Студент 
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Subject> Subjects { get; set; }
}

// Учебный предмет
public class Subject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Teacher Teacher { get; set; }
    public int Hours { get; set; }
    public List<Topic> Topics { get; set; }
}

// Учебный предмет, который выбрал студент
public class StudentSubject
{
    public int StudentId { get; set; }
    public int SubjectId { get; set; }
}

public class ApiDbContext : DbContext
{
    // Список студентов
    public DbSet<Student> Students { get; set;}

    // Список учебных предметов
    public DbSet<Subject> Subjects { get; set; }

    // Список учебных предметов, выбранных студентами
    public DbSet<StudentSubject> StudentSubjects { get; set;  на }

    //...
}

Как с помощью LINQ to SQL получить список предметов (Subjects в классе Student), которые выбрал студент? Понятно, что можно отфильтровать StudentSubjects по StudentId, а дальше?

P.S. Смущает то, что добавление связей "многие-ко-многим" приводит к добавлению дополнительных столбцов в таблицу БД, которые мне не нужны. Например, к таблице Subjects добавляется столбец StudentsId, а ведь один предмет могут выбрать несколько студентов.


Ответы (1 шт):

Автор решения: Alexander Petrov

Для создания связи многие-ко-многим (many-to-many) достаточно в сущностях создать навигационные свойства-коллекции.
В классе Student делаем свойство List<Subject> Subjects.
В классе Subject делаем свойство List<Student> Students.

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Subject> Subjects { get; set; }
}
public class Subject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Student> Students { get; set; }
}
public class ApiDbContext : DbContext
{
    public DbSet<Student> Students { get; set; }
    public DbSet<Subject> Subjects { get; set; }
    // ...
}

При этом в БД автоматически будут созданы три таблицы: Students, Subjects и StudentSubject.
Теперь при добавлении сущностей в навигационные свойства связи между ними будут создавать тоже автоматически.


Возможно, у вас возникли сложности при получении связанных сущностей.
Например:

var student = db.Students.First(x => x.Name == "StudentA");

При выполнении этих запросов навигационные свойства окажутся пустыми. Дело в том, что связанные данные не загружаются по умолчанию. Это нужно сделать явно.
Loading Related Data

Самый распространённый способ - Eager Loading.
Для этого добавим метод Include в запрос:

var student = db.Students.Include(x => x.Subjects).First(x => x.Name == "StudentA");

Теперь предметы будут загружены.


Смущает то, что добавление связей "многие-ко-многим" приводит к добавлению дополнительных столбцов в таблицу БД, которые мне не нужны.

Это столбцы нужны движку реляционной СУБД. Без них невозможно связать данные из разных таблиц между собой.


Подробнее смотрите документацию:
Relationships: Many-to-many.
Changing Foreign Keys and Navigations: Many-to-many relationships.

Промежуточную сущность при желании можно создать и использовать. Но обычно это делают только при добавлении в неё дополнительных данных:
Join entities with payloads.

Можете ознакомиться с туториалом EF Core with MVC. В нём показан пример применения промежуточной сущности с полезной нагрузкой.
Many-to-Many relationships.

→ Ссылка