У меня есть сущности домена POCO, которые сохраняются с помощью Entity Framework 5. Они получены из DbContext с использованием шаблона хранилища и доступны приложению RESTful MVC WebApi через шаблон UoW. Объекты POCO являются прокси и загружаются с отложенным доступом.
Я конвертирую свои сущности в DTO перед тем, как отправить их клиенту. Я использую Automapper для этого, и, похоже, он работает нормально, когда Automapper отображает POCO прокси в DTO, сохраняя свойства навигации без изменений. Я использую следующее сопоставление для этого:
Mapper.CreateMap<Client, ClientDto>();
Пример объекта Domain/DTO:
[Serializable]
public class Client : IEntity
{
public int Id { get; set; }
[Required, MaxLength(100)]
public virtual string Name { get; set; }
public virtual ICollection<ClientLocation> ClientLocations { get; set; }
public virtual ICollection<ComplianceRequirement> DefaultComplianceRequirements { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
public class ClientDto : DtoBase
{
public int Id { get; set; }
[Required, MaxLength(100)]
public string Name { get; set; }
public ICollection<ClientLocation> ClientLocations { get; set; }
public ICollection<ComplianceRequirementDto> DefaultComplianceRequirements { get; set; }
public ICollection<Note> Notes { get; set; }
}
Теперь я пытаюсь обновить свой контекст, используя DTO, отправленные обратно с провода. У меня возникают конкретные проблемы с правильной работой навигационных свойств/связанных объектов. Отображение для этого я использую:
Mapper.CreateMap<ClientDto, Client>()
.ConstructUsing((Func<ClientDto, Client>)(c => clientUow.Get(c.Id)));
Выше clientUow.Get() ссылается на DbContext.Set.Find(), так что я получаю отслеживаемый прокси-объект POCO от EF (который содержит все связанные сущности также в качестве прокси).
В моем методе контроллера я делаю следующее:
var client = Mapper.Map<ClientDto, Client>(clientDto);
uow.Update(client);
Клиент успешно сопоставлен как прокси-объект POCO, однако его связанные сущности/навигационные свойства заменяются новым (непрокси) сущностью POCO со значениями свойств, скопированными из DTO.
Выше uow.Update() в основном относится к функции, которая выполняет постоянную логику, которая у меня есть как:
_context.Entry<T>(entity).State = System.Data.EntityState.Modified;
_context.SaveChanges();
Вышеупомянутое не сохраняется, даже сохраняется сущность, не говоря уже о связанных. Я пробовал варианты отображений и различные способы сохранения, используя отсоединение/состояния, но всегда получаю исключения "объект с таким же ключом уже существует в ObjectStateManager".
Я посмотрел на бесчисленное множество других потоков и просто не могу заставить все это работать с Automapper. Я могу получить прокси-объект из контекста и вручную пройти через свойства, обновляя их из DTO, но я использую Automapper для сопоставления домена → DTO, и было бы более элегантно использовать его для обратного, поскольку мои DTO похожи на мои доменные объекты в значительной степени.
Есть ли учебный способ работы с Automapper с EF с помощью доменных объектов /DTO, имеющих навигационные свойства, которые также необходимо обновлять одновременно?
ОБНОВИТЬ:
var originalEntity = _entities.Find(entity.Id);
_context.Entry<T>(originalEntity).State = System.Data.EntityState.Detached;
_context.Entry<T>(entity).State = System.Data.EntityState.Modified;
Вышеприведенная логика постоянства обновляет "корневой" прокси-объект EF в контексте, однако любые связанные объекты не обновляются. Я предполагаю, что это связано с тем, что они не сопоставляются с прокси-объектами EF, а являются простыми объектами домена. Помощь будет наиболее ценится!
ОБНОВЛЕНИЕ: Кажется, что то, что я пытаюсь достичь, на самом деле невозможно с использованием текущей версии EF (5), и что это основное ограничение EF, а не Automapper:
Я полагаю, это снова сделано вручную. Надеюсь, что это помогает кому-то еще, кому интересно то же самое.