在我的ASP.NET Core 2.2中。 MVC项目我有一个主从表单,用于编辑PARENT表,该表也有一个子实体列表(存储在CHILD表中)。
当我在表单上保存修改时,我首先将更改保存到父实体,然后运行以下方法来更新相应的子实体:
private void UpdateChildList(ParentModel model)
{
// lists
var listOld = _parents.Get(model.IdParent).ListChilds;
var listNew = model.ListChilds;
var listNewInt = from x in listNew select x.IdChild;
// 1. remove deleted
var deleteList = new List<int>();
deleteList = listOld.Where(t => !listNewInt.Contains(t.IdChild)).Select(x => x.IdChild).ToList();
foreach (var d in deleteList)
{
var item = listOld.Where(x => x.IdChild == l).First();
_parents.DeleteChild(item);
}
// 2. update existing
var list = from x in listNew
where x.IdChild > 0
select x;
foreach (var item in list)
{
_parents.UpdateChild(item);
}
// 3. add new
list = from x in listNew
where x.IdChild == 0
select x;
foreach (var item in list)
{
_parents.AddChild(item);
}
}
1。删除正常,& 3。添加正常,但是 2 。UPDATING会产生以下错误:
InvalidOperationException:无法跟踪实体类型'Child'的实例,因为已经跟踪了另一个具有相同'{IdId'}关键字值的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看冲突的键值。
注意:我正在使用service.AddScoped
在我的Startup类中添加服务。
编辑:我正在从HTTPPost添加代码(在控制器中):
[HttpPost]
public IActionResult Edit(ParentModel model)
{
// valid check
if (!ModelState.IsValid)
{
/// ... some lists for dropdowns here
return View(model);
}
var item = _parents.Get(model.IdParent);
item.Field1 = model.Field1;
// ... and other fields
// update parent
_parents.Update(item);
// update Child list
UpdateChildList(model);
return RedirectToAction("Index");
}
EDIT2:添加我的代码以删除/更新/添加子记录
public void AddChild(Child item)
{
_dbContext.Add(item);
_dbContext.SaveChanges();
}
public void UpdateChild(Child item)
{
_dbContext.Update(item);
_dbContext.SaveChanges();
}
public void DeleteChild(Child item)
{
_dbContext.Remove(item);
_dbContext.SaveChanges();
}
答案 0 :(得分:1)
操作时:
var listOld = _parents.Get(model.IdParent).ListChilds;
实体框架从上下文中检索信息,并在执行此操作时开始跟踪该实体:
_parents.UpdateChild(item);
您调用:
_dbContext.Update(item);
另一方面,它尝试将该实体再次附加到上下文,并将其EntityState
设置为Modified
,因此您会遇到异常。
当您从数据库中检索信息然后使用它时,您处于所谓的“连接方案”中,并且Entity Framework上下文会跟踪所有检索到的实体。修改此实体的某些数据后,上下文会因为执行修改而将其EntityState
设置为Modified
。因此,然后您可以调用SaveChanges()
方法,该方法将在数据库中构建并执行Update语句。
因此,您有两种选择:
在检索查询中添加.AsNoTracking()
,因此您将不执行跟踪查询,并且不会将任何实体分别添加到数据库上下文中。然后,您将在“已断开连接的场景”中工作,并且可以使用数据库上下文Update()
方法;
通过逐字段映射来更改UpdateChild
方法,因此更改实体中的字段会将EntityState
设置为Modified
。然后完全删除_dbContext.Update(item)
,因为SaveChanges()
将完成整个工作。
要提的另一件事-循环执行SaveChanges()
通常不是一个好主意。这意味着您将(n)个查询发送到数据库,其中(n)是记录数。