给定使用Entity Framework Core和SQL数据库的ASP.NET Core Web应用程序。

尝试更新数据库中的实体时,绝对简单的操作将引发此异常。首先被生产中的错误报告所注意到。

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(string id, [Bind("Group")] EditViewModel model)
{
    if (id != model.Group.Id) return NotFound();

    if (!ModelState.IsValid) return View(model);

    _context.Update(model.Group);
    await _context.SaveChangesAsync();

    return RedirectToAction("Index");
}

在行上抛出异常:_context.Update(model.Group);


显然没有其他实例。当我在该行的断点处停止代码并扩展_context.Group对象的Results属性时,我能够在开发环境中重现异常:
c# - 无法跟踪实体类型...的实例,因为已经跟踪了具有相同键的该类型的另一个实例-LMLPHP
可以理解的是,在扩展结果时,它将加载需要更新的实例,这就是引发异常的原因。但是部署的生产环境又如何呢?

谢谢您的帮助!

UPDATE1 Group模型:
public class Group
{
    [Display(Name = "ID")]
    public string Id { get; set; }

    public virtual Country Country { get; set; }

    [Required]
    [Display(Name = "Country")]
    [ForeignKey("Country")]
    public string CountryCode { get; set; }

    [Required]
    [Display(Name = "Name")]
    public string Name { get; set; }
}

UPDATE2 基于@Mithgroth的答案,我能够覆盖_context.Update()函数,以免每次使用时都需要try-catch:
public interface IEntity
{
    string Id { get; }
}

public override EntityEntry<TEntity> Update<TEntity>(TEntity entity)
{
    if (entity == null)
    {
        throw new System.ArgumentNullException(nameof(entity));
    }

    try
    {
        return base.Update(entity);
    }
    catch (System.InvalidOperationException)
    {
        var originalEntity = Find(entity.GetType(), ((IEntity)entity).Id);
        Entry(originalEntity).CurrentValues.SetValues(entity);
        return Entry((TEntity)originalEntity);
    }
}

最佳答案

请改用以下内容:

var group = _context.Group.First(g => g.Id == model.Group.Id);
_context.Entry(group).CurrentValues.SetValues(model.Group);
await _context.SaveChangesAsync();

异常可能是由许多不同的情况引起的,但事实是,您试图更改已被不同标记的对象的状态。

例如,这将产生相同的异常:
var group = new Group() { Id = model.Id, ... };
db.Update(group);

否则,您可能已经分离了N层子级,这就是所有可能。

这样可以确保您只是覆盖现有实体的值。

10-05 20:50
查看更多