更新分离的对象导致违反主键异常

时间:2018-10-21 12:59:24

标签: asp.net-mvc entity-framework entity-framework-6

尽管违反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

  5. 当我尝试保存异常时会引发

      

    违反PRIMARY KEY约束'PK_dbo.CourseTypeCourses'。无法在对象'dbo.CourseTypeCourses'中插入重复的密钥。

我在做什么错?预先感谢

1 个答案:

答案 0 :(得分:1)

我必须假定“ dbo.CourseTypeCourses”是同时包含Cources和CourceResources键的中间表,并且您确实试图添加已经存在的记录。

请注意,您在GET方法中对CourceResources进行的分离操作不会传递该方法的结尾,因为您没有在db对象上保存任何更改(坦率地说,您不应在这种情况下使用所有)。因此,当您在POST方法中获取数据库对象时,就可以像开始时那样获取它们。

要进一步解释这一点,请注意Controller类实现IDisposable接口,在每个调用的末尾放置实例,并在每个调用的开始运行默认构造函数。因此,控制器属性(即db对象)的任何更改都不会通过调用的结尾。您的db对象以及控制器将在每次调用时进行初始化。

在我看来,进行您想做的事情的方法要简单得多:

  • 您无需在GET方法中对db对象进行任何更改。
  • 在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");
}

快乐的编码!