我遇到一个问题,使用AutoMapper(这是一项出色的技术)将业务对象映射到DTO,我从集合中继承了抽象基类。
以下是我的目标:
abstract class Payment
class CashPayment : Payment
class CreditCardPayment : Payment
我还有一个发票对象,其中包含一系列付款,如下所示:
public class Invoice
{
... properties...
public ICollection<Payment> Payments { get; set; }
}
我也有相应的每个对象的DTO版本。
DtoInvoice对象定义为:
[DataContract]
public class DtoInvoice
{
...properties...
[DataMember]
public List<DtoPayment> Payments { get; set; }
}
这就是我的Mapper定义:
Mapper.CreateMap<Invoice, DtoInvoice>();
Mapper.CreateMap<Payment, DtoPayment>()
.Include<CashPayment, DtoCashPayment>()
.Include<CreditCardPayment, DtoCreditCardPayment>();
Mapper.CreateMap<CashPayment, DtoCashPayment>();
Mapper.CreateMap<CreditCardPayment, DtoCreditCardPayment>();
执行映射的代码如下所示:
var invoice = repo.GetInvoice(invoiceId);
var dtoInvoice = Mapper.Map<Invoice, DtoInvoice>(invoice);
因此,例如,如果我的发票对象包含特定付款的集合(比如1现金和1张信用卡),当映射器尝试映射它们时,我会收到错误,即无法创建抽象类付款。如果我从Payment对象中删除了abstract关键字,那么代码可以工作,但我只收到Payment对象的集合,我没有得到他们的特定对象(现金和信用卡付款)。
所以问题是:如何让AutoMapper映射特定的支付类型而不是基类?
更新
我做了一些挖掘并认为我发现了一个问题,但我不确定如何使用AutoMapper解决这个问题。我认为这更像是EF的事情,而不是AutoMapper的错。 : - )
在我的代码中,我使用延迟加载的实体框架4代理POCO。
因此,当我尝试映射从EF返回的实体(代理POCO)时,它会获得如下有趣的类型:
System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2
所以我的理论是,当AutoMapper尝试将CashPayment映射到DtoCashPayment并且传入的付款属于代理类型时,AutoMapper将其视为“不匹配”,然后映射通用付款类型。但由于Payment是一个抽象类AutoMapper炸弹,其中包含“System.InvalidOperationException:无法创建抽象类的实例”。异常。
所以问题是:我有没有办法使用AutoMapper将EF POCO代理对象映射到Dtos。
答案 0 :(得分:15)
由于我刚刚遇到与EF4 POCO代理相同的问题,这个答案有点晚了。
我使用自定义转换器解决了它,该转换器调用Mapper.DynamicMap<TDestination>(object source)
来调用运行时类型转换,而不是.Include<TOtherSource, TOtherDestinatio>()
。
它适用于我。
在您的情况下,您将定义以下转换器:
class PaymentConverter : ITypeConverter<Payment, DtoPayment> {
public DtoPayment Convert( ResolutionContext context ) {
return Mapper.DynamicMap<DtoPayment>( context.SourceValue );
}
}
然后:
Mapper.CreateMap<Payment, DtoPayment>().ConvertUsing<PaymentConverter>();
Mapper.CreateMap<CashPayment, DtoCashPayment>();
Mapper.CreateMap<CreditCardPayment, DtoCreditCardPayment>();
答案 1 :(得分:15)
我也试过Olivier的例子并得到了相同的StackOverflow错误。我也试过subkamran的解决方案,但不是运气,因为我没有使用实体模型代码生成的基类。 Automapper仍在爆炸。在找到更好的解决方案之前,我只是在创建Context对象时将Context设置为不创建Proxies。
model.Configuration.ProxyCreationEnabled = false;
model.Configuration.LazyLoadingEnabled = true;
我也希望看到问题的答案,或许使用构建到Automapper中的东西...
更新:Automapper的预发行版纠正了这个问题,并允许映射覆盖DynamicProxy而无需额外配置。
这个版本适用于2.2.1
答案 2 :(得分:13)
在Olivier的回复基础上,我无法让他在我的上下文中工作......它继续进行无限循环并抛出StackOverflowException。
在此示例中,AbstractClass
是我的基类,AbstractViewModel
是我的基本视图模型(未标记为abstract
)。
然而,我确实使用这个看起来很糟糕的转换器来工作:
public class ProxyConverter<TSource, TDestination> : ITypeConverter<TSource, TDestination>
where TSource : class
where TDestination : class
{
public TDestination Convert(ResolutionContext context)
{
// Get dynamic proxy base type
var baseType = context.SourceValue.GetType().BaseType;
// Return regular map if base type == Abstract base type
if (baseType == typeof(TSource))
baseType = context.SourceValue.GetType();
// Look up map for base type
var destType = (from maps in Mapper.GetAllTypeMaps()
where maps.SourceType == baseType
select maps).FirstOrDefault().DestinationType;
return Mapper.DynamicMap(context.SourceValue, baseType, destType) as TDestination;
}
}
// Usage
Mapper.CreateMap<AbstractClass, AbstractViewModel>()
.ConvertUsing(new ProxyConverter<AbstractClass, AbstractViewModel>());
因此,DerivedClassA
会正常映射,但DynamicProxy_xxx
也会正确映射,因为此代码会检查其基本类型(DerivedClassA
)。
拜托,请告诉我,我没有做这个疯狂的查找垃圾。我不太了解AutoMapper来正确修复Olivier的答案。
答案 3 :(得分:10)
我遇到了与Entity Framework代理相同的问题,但不想切换到AutoMapper的预发布版本。对于2.2.0版本,我发现了一个简单但有点难看的工作。我试图从DTO转到现有的EF代理对象,并且错过了丢失丑陋的代理类名称的映射。我的解决方案是使用指定我手动映射的实际具体类型的重载:
Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
答案 4 :(得分:6)
我在MVC应用程序中将动态EF代理映射到ViewModel时遇到了同样的问题。
我找到了一个简单的解决方案,使用 Mapper.DynamicMap()来解决这个问题。这是我的代码:
从动态代理转换为ViewModel类:
// dynamic proxy instance
WebService webService = _repWebService.GetAll().SingleOrDefault(x => x.Id == id);
//mapping
FirstStepWebServiceModel model = Mapper.DynamicMap<FirstStepWebServiceModel>(webService);
从ViewModel类转换为EF动态代理:
[HttpPost]
public ActionResult FirstStep(FirstStepWebServiceModel input)
{
// getting the dynamic proxy from database
WebService webService = _repWebService.GetAll().Single(x => x.Id == input.WebServiceId);
// mapping the input ViewModel class to the Dynamic Proxy entity
Mapper.DynamicMap(input, webService);
}
希望这个例子可以帮助你