由于另一个实例具有相同的ID错误,因此无法跟踪类型的实例 [英] instance of type cannot be tracked because of another instance with same ID error

查看:88
本文介绍了由于另一个实例具有相同的ID错误,因此无法跟踪类型的实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有EF Core的.net core 2.1 mvc应用程序,在这里我使用自动映射器将viewmodel与domainmodels匹配.在我的编辑方法中,出现错误:

I have a .net core 2.1 mvc application with EF Core where I use automapper to match viewmodels with domainmodels. In my edit method I get the error :

InvalidOperationException:实体类型的实例无法跟踪票证",因为具有{'ID'}的相同键值已被跟踪.

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

这里的其他几个主题没有解决我的问题.

The several other topics here did not solve my problem.

我的编辑方法:

 [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,但是最终的映射似乎没有将viewmodel的值映射到_dbcontext实体.

The InvalidOperationException is now solved, but the final mapping doesn't seem to map the values of the viewmodel to the _dbcontext entity.

推荐答案

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

You are loading the domain item, however you are using the wrong automapper call:

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

这应该是:

_mapper.Map(ticketViewModel, ticketToUpdate);

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

The first method takes the values from the view model and loads them into a brand new instance of the Entity and assigns it to the ticketToUpdate reference previously loaded. When you go to update that reference, the dbContext behind your unit of work is already tracking the entity with the same ID so you get the error. (The updated reference is treated as a new entity)

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

The second Map call example copies the values from the ViewModel over into the entity referenced by the ticketToUpdate. The resulting reference is pointing at the original entity which gets the new values, and the DbContext will save those changes.

**一个简单的测试,概述了Map调用的行为差异.如果 Map(源,目标)调用未复制您期望的值,请检查您的映射以确保2向转换正确.

** A simple test to outline the behaviour difference with the Map call. If the Map(source, destination) call is not copying over the values you expect, check your mappings to ensure the 2-way conversion is correct.

[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被视为一个新的,未跟踪的实体.

Here the "newA" represents a reference to an entity pulled from a dbContext. We take a 2nd reference to that same entity to compare later. (altReferenceA). If we call newA = mapper.Map<ClassA>(cloneB) it is now a new reference and this results in the exception with EF. EF is tracking the entity still pointed to by altReferenceA. newA is treated as a new, untracked entity.

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

In the second pass we reset the variables and using mapper.Map(cloneB, newA) we copy the value from B to A, both references are updated since they still point to the same object. The tracked entity is updated and can be saved. If the values from B do not get written to newA then I would suspect something is wrong with the mapping configuration from B to A. If the values are being updated in the entity but the entity is not persisting the changes, then I would look at what the Commit() method in your unit of work is trying to do.

这篇关于由于另一个实例具有相同的ID错误,因此无法跟踪类型的实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆