如何在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();
然后我就可以对数据库进行更新。如果我添加这一行(稍后需要),就会得到异常。
谁能给我一些建议,我做错了什么?
您正在做的是在您的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;
}
这就是你在上下文上得到冲突的方式