I am using Automapper to map between Entity and ViewModel object (in both directions). The model uses EF4 DbContext POCOs and needs LazyLoading (and therefore Proxy Generation) enabled.
I have come across a problem attempting to update an existing entity from a viewmodel. When I call Mapper.Map(vm, entity), Automapper throws an exception. My question is: how are you supposed to work with EF Proxy objects using Automapper?
The code looks (simplified) like this:
public class MyEntity
{
public int Id {get;set;}
public int Name {get;set;}
}
public class ViewModel
{
public int Id {get;set;}
public int Name {get;set;}
}
Mapper.CreateMap<MyEntity, ViewModel>();
Mapper.CreateMap<ViewModel, MyEntity>();
public ActionResult Edit(ViewModel vm)
{
MyEntity entity = db.MyEntities.Find(vm.Id);
Mapper.Map(vm, entity);
db.Entry(entity).State = EntityState.Modified;
db.SaveChanges();
}
When I call Mapper.Map(vm, entity) to update the existing entity object, I get the exception:
'Mapper.Map(viewModel, resultSet)' threw an exception of type 'AutoMapper.AutoMapperMappingException'
base {System.Exception}: {"Missing type map configuration or unsupported mapping.\n\nMapping types:\r\nResultSetView -> ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\r\nSciensus.Applications.ClinicalStudies.Web.Areas.Patient.Models.ResultSetView -> System.Data.Entity.DynamicProxies.ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\n\nDestination path:\nResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\n\nSource value:\nSciensus.Applications.ClinicalStudies.Web.Areas.Patient.Models.ResultSetView"}
Context: {Trying to map ResultSetView to ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2.}
Message: "Missing type map configuration or unsupported mapping.\n\nMapping types:\r\nResultSetView -> ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\r\nSciensus.Applications.ClinicalStudies.Web.Areas.Patient.Models.ResultSetView -> System.Data.Entity.DynamicProxies.ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\n\nDestination path:\nResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\n\nSource value:\nSciensus.Applications.ClinicalStudies.Web.Areas.Patient.Models.ResultSetView"
StackTrace: ""
I looked at the AutoMapper source Code:
public TDestination Map<TSource, TDestination>(TSource source, TDestination destination)
{
return Map(source, destination, opts => { });
}
public TDestination Map<TSource, TDestination>(TSource source, TDestination destination, Action<IMappingOperationOptions> opts)
{
Type modelType = typeof(TSource);
Type destinationType = (Equals(destination, default(TDestination)) ? typeof(TDestination) : destination.GetType());
return (TDestination)Map(source, destination, modelType, destinationType, opts);
}
Problems occurred in this place:
Type destinationType = (Equals(destination, default(TDestination)) ? typeof(TDestination) : destination.GetType());
So the change that do not have a problem:
Mapper.Map(vm, entity,typeof(ViewModel),typeof(MyEntity));
As you suspect, I believe you are getting this exception, because AutoMapper doesn't have a map for the proxy class created by Lazy Loading (ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2
) which derives from your ResultSet
entity.
What you might try is the following:
public ActionResult Edit(ViewModel vm)
{
// This returns the entity proxy
MyEntity oldEntity = db.MyEntities.Find(vm.Id);
// i.e. Create a 'plain' Entity, not a proxy.
MyEntity newEntity = Mapper.Map<ViewModel, MyEntity>(vm);
db.Entry(oldEntity).CurrentValues.SetValues(newEntity);
// I don't think you need this now.
// db.Entry(entity).State = EntityState.Modified;
db.SaveChanges();
}