无法跟踪类型的实例,因为另一个实例具有相同的 ID 错误

instance of type cannot be tracked because of another instance with same ID error

我有一个带有 EF Core 的 .net core 2.1 mvc 应用程序,我在其中使用 automapper 将视图模型与域模型相匹配。在我的编辑方法中出现错误:

InvalidOperationException: The instance of entity type 'Ticket' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked.

这里的其他几个题目都没有解决我的问题

我的编辑方法:

 [HttpPost]
 [ValidateAntiForgeryToken]
 public async Task<IActionResult> Edit(int id, TicketViewModel ticketViewModel)
    {
    Ticket ticketToUpdate = await _unitOfWork.Ticket.Get(id); // I implement unit of work 

    // some logic for checks
    // ...

        if (ModelState.IsValid)
        {
            try
            {
                // mapping  Ticket viewmodel to Ticket Domain Model
                ticketViewModel = _mapper.Map<TicketViewModel>(ticketToUpdate);

                // update some properties on viewmodel

                _mapper.Map(ticketViewModel, ticketToUpdate); // doesn't map values.

                _unitOfWork.Ticket.Update(ticketToUpdate); //No longer fails
                await _unitOfWork.Commit();
            }
            catch (DbUpdateConcurrencyException)
            {
                return NotFound();
            }
        return RedirectToAction(nameof(Index));
    }

我的映射:

CreateMap<TicketViewModel, Ticket>()
.ForMember(x => x.ID, x => x.MapFrom(y => y.Ticket.ID))
.ForMember(x => x.Title, x => x.MapFrom(y => y.Ticket.Title))
.ForMember(x => x.Description, x => x.MapFrom(y => y.Ticket.Description))


CreateMap<Ticket, TicketViewModel>()
.ForPath(x => x.Ticket.ID, x => x.MapFrom(y => y.ID))
.ForPath(x => x.Ticket.Title, x => x.MapFrom(y => y.Title))
.ForPath(x => x.Ticket.Description, x => x.MapFrom(y => y.Description))

编辑:InvalidOperationException 现已解决,但最终映射似乎并未将视图模型的值映射到 _dbcontext 实体。

您正在加载域项目,但是您使用了错误的自动映射器调用:

ticketToUpdate = _mapper.Map<Ticket>(ticketViewModel);

这应该是:

_mapper.Map(ticketViewModel, ticketToUpdate);

第一种方法从视图模型中获取值并将它们加载到实体的全新实例中,并将其分配给之前加载的 ticketToUpdate 引用。当您更新该引用时,您的工作单元背后的 dbContext 已经在跟踪具有相同 ID 的实体,因此您会收到错误消息。 (更新后的引用被视为新实体)

第二个 Map 调用示例将 ViewModel 中的值复制到 ticketToUpdate 引用的实体中。生成的引用指向获取新值的原始实体,DbContext 将保存这些更改。

** 编辑:一个简单的测试来概述与 Map 调用的行为差异。如果 Map(source, destination) 调用没有复制您期望的值,请检查您的映射以确保双向转换正确。

[Test]
public void TestCopyOver()
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<ClassA, ClassB>()
            .ForMember(x => x.MyName, x => x.MapFrom(y => y.Name))
            .ForMember(x => x.MyOtherName, x => x.MapFrom(y => y.OtherName));
        cfg.CreateMap<ClassB, ClassA>()
            .ForMember(x => x.Name, x => x.MapFrom(y => y.MyName))
            .ForMember(x => x.OtherName, x => x.MapFrom(y => y.MyOtherName));
    });

    var mapper = config.CreateMapper();

    ClassA newA = new ClassA { Name = "Fred", OtherName = "Astaire" };
    ClassA altReferenceA = newA;
    Assert.AreSame(newA, altReferenceA, "References don't match.");
    var cloneB = mapper.Map<ClassB>(newA);
    cloneB.MyOtherName = "Rogers";

    newA = mapper.Map<ClassA>(cloneB);
    Assert.AreEqual("Rogers", newA.OtherName);
    Assert.AreEqual("Astaire", altReferenceA.OtherName); // original object not updated.
    Assert.AreNotSame(newA, altReferenceA); // now point to 2 different objects

    //Reset...

    newA = new ClassA { Name = "Fred", OtherName = "Astaire" };
    altReferenceA = newA;
    Assert.AreSame(newA, altReferenceA, "References don't match.");
    cloneB = mapper.Map<ClassB>(newA);
    cloneB.MyOtherName = "Rogers";

    mapper.Map(cloneB, newA);
    Assert.AreEqual("Rogers", newA.OtherName);
    Assert.AreEqual("Rogers", altReferenceA.OtherName); // Original object updated.
    Assert.AreSame(newA, altReferenceA); // Still point to same reference.
}

这里的 "newA" 表示对从 dbContext 中提取的实体的引用。我们对同一实体进行第二次引用以便稍后进行比较。 (altReferenceA)。如果我们调用 newA = mapper.Map<ClassA>(cloneB) 它现在是一个新的引用,这会导致 EF 出现异常。 EF 正在跟踪 altReferenceA 仍然指向的实体。 newA 被视为新的、未跟踪的实体。

在第二遍中,我们重置变量并使用 mapper.Map(cloneB, newA) 我们将值从 B 复制到 A,两个引用都被更新,因为它们仍然指向同一个对象。跟踪的实体已更新并可以保存。如果 B 的值没有写入 newA 那么我会怀疑从 B 到 A 的映射配置有问题。如果实体中的值正在更新但实体没有持久化更改,那么我会看看您的工作单元中的 Commit() 方法试图做什么。