如何在EntityFramework 6中更新带有子细节的父对象?

本文关键字:细节 对象 EntityFramework 更新 | 更新日期: 2023-09-27 18:01:39

我有一个SPA ASP。. NET WebAPI应用程序,使用EF6。应用程序有两个父子实体,我在更新时遇到了问题。代码的目的是,当用户更改前端代码时,目标及其详细信息将被发送到控制器。然后,控制器检查是否有任何细节发生了变化,并对其进行处理。现在我已经注释了这部分代码,因为我甚至不能得到最简单的更新工作。下面是我得到的错误:

{"附加类型为'Entities.Models.Core.ObjectiveDetail'的实体失败,因为另一个相同类型的实体已经有相同的主键值。这可能发生在使用'Attach'方法或将实体的状态设置为"未更改"或"修改"(如果有)图中的实体具有冲突的键值。这可能是因为有些实体是新的,尚未收到数据库生成键值。在这种情况下,使用'Add'方法或'Added'实体状态来跟踪图形,然后将非新实体的状态设置为"未修改"或"修改"视情况而定。"}

我有两个类:

public class Objective
{
    public Objective()
    {
        this.ObjectiveDetails = new HashSet<ObjectiveDetail>();
    }
    public int ObjectiveId { get; set; }
    public string Text { get; set; }
    public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
public partial class ObjectiveDetail
{
    public int ObjectiveDetailId { get; set; }
    public int ObjectiveId { get; set; }
    public string Text { get; set; }
    public virtual Objective Objective { get; set; }
}

我的控制器是这样的:

    // PUT: api/Objective/5 Updating
    [ResponseType(typeof(void))]
    public async Task<IHttpActionResult> Put(int id, Objective objective)
    {
        try
        {
            // I get an exception when I uncomment the following line
            //
            //
            // var oldObj = db.ObjectiveDetails.Where(t => t.ObjectiveId == id).ToList();
            var newObj = objective.ObjectiveDetails.ToList();
            //var upd = newObj
            //    .Where(wb => oldObj
            //    .Any(db1 =>
            //       (db1.ObjectiveDetailId == wb.ObjectiveDetailId) &&
            //           (db1.Number != wb.Number || !db1.Text.Equals(wb.Text))))
            //           .ToList();
            //var add = newObj
            //    .Where(wb => oldObj
            //        .All(db2 => db2.ObjectiveDetailId != wb.ObjectiveDetailId))
            //    .ToList();
            //var del = oldObj
            //    .Where(db2 => newObj
            //        .All(wb => wb.ObjectiveDetailId != db2.ObjectiveDetailId))
            //    .ToList();
            //foreach (var objectiveDetail in upd)
            //{
            //    db.Entry(objectiveDetail).State = EntityState.Modified;
            //}
            //foreach (var objectiveDetail in add)
            //{
            //    db.ObjectiveDetails.Add(objectiveDetail);
            //}
            //del.ForEach(_obj => db.ObjectiveDetails.Remove(_obj));
            // I tried the following but it did not work
            //
            //db.Objectives.Attach(objective);
            //db.Entry(objective).State = EntityState.Modified;
            DbEntityEntry dbEntityEntry = db.Entry(objective);
            if (dbEntityEntry.State == EntityState.Detached)
            {
                db.Objectives.Attach(objective);
            }
            dbEntityEntry.State = EntityState.Modified;
            await db.SaveChangesAsync();
            return Ok(objective);
        }
        catch (Exception e)
        {
            return NotFound();
        }
    }

如果我删除行:

var oldObj = db.ObjectiveDetails.Where(t => t.ObjectiveId == id).ToList();

然后我就可以对数据库进行更新。如果我添加这一行(稍后需要),就会得到异常。

谁能给我一些建议,我做错了什么?

如何在EntityFramework 6中更新带有子细节的父对象?

您正在做的是在您的DbContext中加载db实体。然后你试图再次将相同的实体附加到上下文,所以你得到了一个冲突。

你可以尝试在不同的上下文中加载旧的实体,并将它们用作引用,这样可以避免这种冲突。

另一个解决方案是修改已经加载的实体(oldObj的实体),它们已经在您的上下文中,而不是尝试以不同的状态再次添加相同的实体。

你也可以分离或直接加载你的oldObject,就像这里解释的。

理想情况下,您应该在SPA中进行更改跟踪。通过这种方式,您不需要加载旧实体并将它们与新实体进行比较。使用这种方法,应用程序可能会意外地覆盖其他人的更改。

这个代码:

var oldObj = db.ObjectiveDetails.Where(t => t.ObjectiveId == id).ToList();

加载DB中的所有objectiveDetails并将它们附加到当前上下文。

这段代码附加了更新的目标细节,当已经有一个具有相同键的对象(在前一行代码中加载的对象)时,尝试附加一个对象(修改过的对象):

foreach (var objectiveDetail in upd)
{
   db.Entry(objectiveDetail).State = EntityState.Modified;
}

这就是你在上下文上得到冲突的方式