我正在使用Automapper在Entity和ViewModel对象之间进行映射(在两个方向上)。该模型使用EF4 DbContext POCO并需要启用LazyLoading(因此代理生成)。
我在尝试从viewmodel更新现有实体时遇到了问题。当我调用Mapper.Map(vm,entity)时,Automapper会抛出异常。我的问题是:您如何使用Automapper处理EF代理对象?
代码看起来(简化)如下:
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();
}
当我调用Mapper.Map(vm,entity)来更新现有的实体对象时,我得到了异常:
'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: ""
答案 0 :(得分:6)
我查看了AutoMapper源代码:
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);
}
这个地方出现了问题:
Type destinationType = (Equals(destination, default(TDestination)) ? typeof(TDestination) : destination.GetType());
所以没有问题的改变:
Mapper.Map(vm, entity,typeof(ViewModel),typeof(MyEntity));
答案 1 :(得分:3)
正如您所怀疑的那样,我相信您正在获得此异常,因为AutoMapper没有由Lazy Loading(ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2
)创建的代理类的映射,该映射派生自您的ResultSet
实体。< / p>
您可能会尝试以下内容:
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();
}
答案 2 :(得分:1)
所以我最终做的是滚动我自己的IMappable接口和一个简单的通用映射实用程序来支持双向映射。根据映射的复杂性,所需的代码最终可能比使用Automapper要小。代码如下:
public interface IMappable<TEntity, TViewModel>
{
void Map(TEntity source, TViewModel target);
void Map(TViewModel source, TEntity target);
}
public class ModelMapper<TEntity, TViewModel> where TEntity : new() where TViewModel : IMappable<TEntity, TViewModel>, new()
{
public static TViewModel Map(TEntity source)
{
TViewModel target = new TViewModel();
Map(source, target);
return target;
}
public static TEntity Map(TViewModel source)
{
TEntity target = new TEntity();
Map(source, target);
return target;
}
public static void Map(TEntity source, TViewModel target)
{
new TViewModel().Map(source, target);
}
public static void Map(TViewModel source, TEntity target)
{
new TViewModel().Map(source, target);
}
}
My ViewModels根据需要为尽可能多的Entity类实现IMappable,实现Map方法来处理每个方向的数据传输:
public class AddressView : IMappable<Address, AddressView>
{
public long AddressId { get; set; }
...
#region "IMappable members"
public void Map(Address source, AddressView target)
{
target.AddressId = source.AddressId;
target.City = source.City;
target.FullAddress = source.FullAddress;
if (source.CountryId.HasValue)
{
target.Country = source.Country.Name;
target.CountryId = source.CountryId;
}
target.District = source.District;
target.Postcode = source.Postcode;
target.StreetName = source.StreetName;
}
public void Map(AddressView source, Address target)
{
target.AddressId = source.AddressId;
target.City = source.City;
target.CountryId = source.CountryId;
target.District = source.District;
target.Postcode = source.Postcode;
target.StreetName = source.StreetName;
}
#endregion
}