Второй EDIT в ответе @ nemesv или настройка AutoMapper - это, на мой взгляд, путь.Вы должны принять его ответ.Я только добавляю объяснение, почему ваш код не работает (но ваш код с установкой состояния дважды работает).Прежде всего, проблема не имеет ничего общего с AutoMapper , вы получите то же поведение, когда будете устанавливать свойства вручную.
Важно знать, что установка состояния (Entry(dest).State = EntityState.Modified
) не только устанавливает некоторый внутренний флаг в контексте, но установщик свойства для State
вызывает на самом деле некоторые сложные методы, особенно это вызывает DbContext.ChangeTracker.DetectChanges()
(если вы не отключите AutoDetectChangesEnabled
).
Итак, что происходит в первом случае:
// ...
Mapper.Map(source, dest);
dest.Company = oldCompany;
// at this point the state of dest EF knows about is still the state
// when you loaded the entity from the context because you are not working
// with change tracking proxies, so the values are at this point:
// dest.CompanyId = null <- this changed compared to original value
// dest.Company = company <- this did NOT change compared to original value
// The next line will call DetectChanges() internally: EF will compare the
// current property values of dest with the snapshot of the values it had
// when you loaded the entity
slaumaContext.Entry(dest).State = System.Data.EntityState.Modified;
// So what did EF detect:
// dest.Company didn't change, but dest.CompanyId did!
// So, it assumes that you have set the FK property to null and want
// to null out the relationship. As a consequence, EF also sets dest.Company
// to null at this point and later saves null to the DB
Что происходит во втором случае:
// ...
Mapper.Map(source, dest);
// Again in the next line DetectChanges() is called, but now
// dest.Company is null. So EF will detect a change of the navigation property
// compared to the original state
slaumaContext.Entry(dest).State = System.Data.EntityState.Modified;
dest.Company = oldCompany;
// Now DetectChanges() will find that dest.Company has changed again
// compared to the last call of DetectChanges. As a consequence it will
// set dest.CompanyId to the correct value of dest.Company
slaumaContext.Entry(dest).State = System.Data.EntityState.Modified;
// dest.Company and dest.CompanyId will have the old values now
// and SaveChanges() doesn't null out the relationship
Итак, это на самом деле нормальное поведение отслеживания изменений, а неошибка в EF.
Одна вещь, которая меня беспокоит, это то, что у вас есть ViewModel, которая, очевидно, имеет свойства, которые вы не используете в представлении.Если ваша ViewModel не будет иметь Company
и CompanyId
, все проблемы исчезнут.(Или настройте хотя бы AutoMapper, чтобы не отображать эти свойства, как показано @nemesv.)