Вы загружаете элемент домена, однако вы используете неправильный вызов автоматического обработчика:
ticketToUpdate = _mapper.Map<Ticket>(ticketViewModel);
Это должно быть:
_mapper.Map(ticketViewModel, ticketToUpdate);
Первый метод получает значения из представления моделирует и загружает их в совершенно новый экземпляр сущности и назначает его ранее загруженной ссылке ticketToUpdate. Когда вы go обновляете эту ссылку, dbContext за вашей единицей работы уже отслеживает сущность с тем же идентификатором, поэтому вы получаете ошибку. (Обновленная ссылка обрабатывается как новая сущность)
Второй пример вызова 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()
в вашей единице работы.