I've got POCO domain entities that are persisted using Entity Framework 5. They are obtained from the DbContext using a repository pattern and are exposed to a RESTful MVC WebApi application through a UoW pattern. The POCO entities are proxies and are lazy loaded.
I am converting my entities to DTOs before sending them to the client. I am using Automapper to do this and it seems to be working fine with Automapper mapping the proxy POCOs to DTOs, keeping the navigation properties intact. I am using the following mapping for this:
Mapper.CreateMap<Client, ClientDto>();
Example of Domain/DTO objects:
[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; }
}
Now I am trying to update my context using DTOs sent back up from the wire. I am having specific trouble with getting the navigational properties/related entities working properly. The mapping for this I'm using is:
Mapper.CreateMap<ClientDto, Client>()
.ConstructUsing((Func<ClientDto, Client>)(c => clientUow.Get(c.Id)));
Above, clientUow.Get() refers to DbContext.Set.Find() so that I am getting the tracked proxy POCO object from EF (that contains all of the related entities also as proxies).
In my controller method I am doing the following:
var client = Mapper.Map<ClientDto, Client>(clientDto);
uow.Update(client);
client successfully is mapped, as a proxy POCO object, however it's related entities/navigational properties are replaced with a new (non-proxy) POCO entity with property values copied from the DTO.
Above, uow.Update() basically refers to a function that performs the persist logic which I have as:
_context.Entry<T>(entity).State = System.Data.EntityState.Modified;
_context.SaveChanges();
The above doesn't persist even persist the entity, let alone related ones. I've tried variations on the mappings and different ways to persist using detaching/states but always get "an object with the same key already exists in the ObjectStateManager" exceptions.
I've had a look at countless other threads and just can't get it all working with Automapper. I can grab a proxy object from the context and manually go through properties updating them from the DTO fine, however I am using Automapper to map domain -> DTO and it would be alot more elegant to use it to do the reverse, since my DTOs resemble my domain objects to a large extent.
Is there a textbook way to handle Automapper with EF, with Domain Objects/DTOs that have navigational properties that also need to be updated at the same time?
UPDATE:
var originalEntity = _entities.Find(entity.Id);
_context.Entry<T>(originalEntity).State = System.Data.EntityState.Detached;
_context.Entry<T>(entity).State = System.Data.EntityState.Modified;
The above persistence logic updates the 'root' EF proxy object in the context, however any related entities are not updated. I'm guessing that this is due to them not being mapped to EF proxy objects but rather plain domain objects. Help would be most appreciated!
UPDATE: It seems that what I'm trying to achieve is not actually possible using the current version of EF(5) and that this is a core limitation of EF and not to do with Automapper:
I guess it's back to doing it manually. Hope this helps someone else who is wondering the same.
ICollection<ClientLocationDto>
andICollection<NoteDto>
in yourClientDto
class? (As you also haveComplianceRequirementDto
). Did you map these Dto's the same way as ClientDto? – Gert Arnold