Обновление отсоединенных объектов вызывает нарушение первичного ключа исключения - PullRequest
0 голосов
/ 21 октября 2018

Хотя нарушение ограничения PRIMARY KEY было дано несколько раз, ни один из них не смог решить мою проблему.Рассмотрим ниже модель.Каждый объект Course может иметь несколько ресурсов, а каждый CourseResource может принадлежать нескольким курсам.

public class Course 
{
    public int Id {get; set;}
    // other properties
    public IList<CourseResourse> CourseResources {get; set;}
}

public class CourseResouce 
{
    public int Id {get; set;}
    // other properties
    public IList<Course> Courses {get; set;}
}

Действие редактирования для модели Course:

public ActionResult Edit(int? id)
{
        if (id == null)
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);

        Course course = db.Courses
            .Include(x => x.CourseResources)
            .Where(x => x.Id == id)
            .AsNoTracking()
            .FirstOrDefault();

        if (course == null)
            return HttpNotFound();

        db.Detach(course.CourseResources);

        CourseVm courseVm = new CourseVm()
        {
            Course = course,
            // other properties
        };

        return View(courseVm);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CourseVm courseVm)
{
    if (ModelState.IsValid)
    {
        IList<int> rsIds = courseVm.Course.CourseResources.Select(x => x.Id).ToList();

        Course c = db.Courses.Find(courseVm.Course.Id);
        if (c.IsNotNull())
        {
            db.Entry(c).State = EntityState.Modified;
            db.SaveChanges();

            foreach (var resourceId in rsIds)
            {
                var rs = db.CourseResources.SingleOrDefault(x => x.Id == resourceId);
                if (rs.IsNotNull())
                    c.CourseResources.Add(rs);
            }

            db.Entry(c).State = EntityState.Modified;
            db.SaveChanges();  // <- Error here
        }

        return RedirectToAction("Index");
    }

Описание:

  1. Я пытался отсоединить дочерние объекты (CourseResources) от course

  2. Очистить ресурсы от полученных Course (ViewModel)

  3. Сохранить курс

  4. Затем добавить новые CourseResourse s к нему

  5. Когда япопытка сохранить исключение будет вызвано

    Нарушение ограничения PRIMARY KEY 'PK_dbo.CourseTypeCourses'.Невозможно вставить дубликат ключа в объект 'dbo.CourseTypeCourses'.

Что я делаю не так?Заранее спасибо

1 Ответ

0 голосов
/ 22 октября 2018

Я должен предположить, что "dbo.CourseTypeCourses" является промежуточной таблицей, которая содержит оба ключа Cources и CourceResources, и что вы действительно пытаетесь добавить запись, которая уже существует.

Обратите внимание, что действие отсоединениявы делаете на CourceResources в методе GET, не пропускаете конец этого метода, потому что вы не сохраняете никаких изменений в вашем объекте db (и, честно говоря, вы вообще не должны идти по этому пути).Поэтому, когда вы получаете объекты базы данных в вашем методе POST, вы получаете их такими, какими они были в начале.

Чтобы объяснить это подробнее, обратите внимание, что класс Controller реализует интерфейс IDisposable, располагающий экземпляр в конце каждого вызова и запускающий конструктор по умолчанию в начале каждого вызова.Таким образом, любые изменения свойств вашего контроллера (то есть объекта db) не проходят конец вызова.Ваш объект db инициализируется при каждом вызове вместе с контроллером.

Способ выполнить то, что вы хотите сделать, на мой взгляд, намного проще:

  • Вам не нужно вносить какие-либо изменения в ваши объекты БД в методе GET.
  • В вашем методе POST получите курс из БД (как и вы), , включая CourseResources.Очистите все предметы из CourseResources и добавьте все новые (как вы)

Так что попробуйте что-то вроде следующего:

public ActionResult Edit(int? id)
{
    if (id == null)
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);

    Course course = db.Courses
        .Include(x => x.CourseResources)
        .Where(x => x.Id == id)
        .AsNoTracking()
        .FirstOrDefault();

    if (course == null)
        return HttpNotFound();

    //db.Detach(course.CourseResources);

    CourseVm courseVm = new CourseVm()
    {
        Course = course,
        // other properties
    };

    return View(courseVm);
}


[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CourseVm courseVm)
{
   if (ModelState.IsValid)
   {
       IList<int> rsIds = courseVm.Course.CourseResources.Select(x => x.Id).ToList();

    Course c = db.Courses
        .Include(x => x.CourseResources)
        .Where(x => x.Id == id)
        .AsNoTracking()
        .FirstOrDefault();

    if (c.IsNotNull())
    {
        c.CourseResources.Clear();

        foreach (var resourceId in rsIds)
        {
            var rs = db.CourseResources.SingleOrDefault(x => x.Id == resourceId);
            if (rs.IsNotNull())
                c.CourseResources.Add(rs);
        }

        db.Entry(c).State = EntityState.Modified;
        db.SaveChanges();
    }

    return RedirectToAction("Index");
}

Веселое кодирование!

...