У одного учителя может быть много предметов, и наоборот, у одного предмета может быть много учителей. То есть здесь mtm ((ef> 5.0). Это именно то, что я сделал. Но при добавлении нового учителя создаются новые предметы. Как это исправить?

Перед добавлением учителя

  • Химия
  • PE
  • Биология

Затем я добавляю нового учителя

    // for example subjects = ["Biology", "Chemistry"] 

    await _db.Teachers.AddAsync(new Teacher
    {
          ...
          Subjects = subjects,
    });

После добавления учителя

  • Химия
  • PE
  • Биология
  • Химия
  • Биология

Модель учителя

public class Teacher : User
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public IList<Subject> Subjects { get; set; }
}

Модель пользователя

public class User
{
    public int Id { get; set; }
    public string Login { get; set; }
}

Предметная модель

public class Subject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<Teacher> Teachers { get; set; }
}
-2
olegshmel 11 Фев 2021 в 21:06

2 ответа

Лучший ответ

Это обычная ошибка веб-приложений и EF, когда вы передаете сущности, или случаи, когда вы создаете новые ссылки на связанные сущности и предполагаете, что EF свяжет их.

Если у вас есть коллекция Subjects, переданная вашему методу, например:

public void CreateTeacher(string name, IEnumerable<Subject> subjects)
{
    var teacher = new Teacher
    {
        Name = name,
        Subjects = subjects
    };
    context.Teachers.Add(teacher);
    context.SaveChanges();
}

Где в списке предметов были химия и биология. Это выглядит достаточно невинно, но между вызовами веб-запросов эти входящие субъекты являются просто десериализованными объектами. Они не известны DbContext, поэтому, когда они связаны с новым Учителем, они рассматриваются как новые предметы. По умолчанию в соглашениях EF столбец с именем Id будет рассматриваться как идентификатор, поэтому в таблицу Subject будут добавлены два новых объекта с именами Chemistry & Biology.

Чтобы избежать такого поведения, нам нужно сделать одно из двух: связать субъекты с DbContext (после проверки, чтобы убедиться, что они еще не связаны) или убедиться, что мы используем только те объекты, которые были извлечены DbContext.

Пример: получение тем из DbContext

public void CreateTeacher(string name, IEnumerable<Subject> subjects)
{
    var subjectIds = subjects.Select(x => x.Id).ToList();
    var dbSubjects = context.Subjects.Where(x => subjectIds.Contains(x.Id)).ToList();
    var teacher = new Teacher
    {
        Name = name,
        Subjects = dbSubjects
    };
    context.Teachers.Add(teacher);
    context.SaveChanges();
}

Обычно мы изменяем метод, чтобы просто передать идентификаторы субъектов и использовать DbContext для их загрузки. Нет необходимости пересылать все поля темы по сети.

Пример: связь с DbContext

public void CreateTeacher(string name, IEnumerable<Subject> subjects)
{
    var subjectIds = subjects.Select(x => x.Id).ToList();
    foreach(var subject in subjects)
    {
        if(!context.Subjects.Local.Any(x => x.Id == subject.Id)
            context.Subjects.Attach(subject);
    }
    var dbSubjects = context.Subjects.Local
        .Where(x => subjectIds.Contains(x => x.Id))
        .ToList();

    var teacher = new Teacher
    {
        Name = name,
        Subjects = dbSubjects
    };
    context.Teachers.Add(teacher);
    context.SaveChanges();
}

Это предполагает, что все поступающие Субъекты действительны и ссылаются на существующую строку в базе данных. Если вы можете смешивать новые предметы, это становится немного сложнее. (проверка идентификаторов субъектов на 0 и т. д.) При переходе к context.Subjects.Local мы не попадаем в базу данных, мы просто проверяем кеш DbContext на наличие загруженных тем перед присоединением темы. После присоединения он будет частью набора .Local, поэтому мы сможем найти наши объекты, не касаясь базы данных.

Как правило, выборка данных из БД безопаснее и проще. Получение сущностей по идентификатору происходит довольно быстро и помогает гарантировать, что состояние данных поддерживает переданные ссылки.

1
Steve Py 11 Фев 2021 в 23:47

Я думаю, что то, что предлагает Сергей, близко к тому, что требуется, поскольку я думаю, вам нужно вынести добавление отношений за пределы добавления записи учителя. В его предложении изменилась лишь вторая из последней строчки.

var teacher=new Teacher
    {
          ...
                   // don' t use this Subjects = subjects,
    });

 _db.Teachers.Add(teacher);
 teacher.Subjects = tvm.Subjects;
 await _db.Teacher.SaveChangesAsync();
-2
Chris 11 Фев 2021 в 19:04
66160304