实体框架核心更新子实体问题

时间:2019-10-08 17:31:48

标签: c# asp.net-core entity-framework-core

在我的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();
}

1 个答案:

答案 0 :(得分:1)

操作时:

var listOld = _parents.Get(model.IdParent).ListChilds;

实体框架从上下文中检索信息,并在执行此操作时开始跟踪该实体:

_parents.UpdateChild(item);

您调用:

_dbContext.Update(item);

另一方面,它尝试将该实体再次附加到上下文,并将其EntityState设置为Modified,因此您会遇到异常。

当您从数据库中检索信息然后使用它时,您处于所谓的“连接方案”中,并且Entity Framework上下文会跟踪所有检索到的实体。修改此实体的某些数据后,上下文会因为执行修改而将其EntityState设置为Modified。因此,然后您可以调用SaveChanges()方法,该方法将在数据库中构建并执行Update语句。

因此,您有两种选择:

  1. 在检索查询中添加.AsNoTracking(),因此您将不执行跟踪查询,并且不会将任何实体分别添加到数据库上下文中。然后,您将在“已断开连接的场景”中工作,并且可以使用数据库上下文Update()方法;

  2. 通过逐字段映射来更改UpdateChild方法,因此更改实体中的字段会将EntityState设置为Modified。然后完全删除_dbContext.Update(item),因为SaveChanges()将完成整个工作。

要提的另一件事-循环执行SaveChanges()通常不是一个好主意。这意味着您将(n)个查询发送到数据库,其中(n)是记录数。

相关问题