Automapper:使用Entity Framework 4 Proxy Pocos在inheritance上映射问题,在集合上映射抽象基类
我有一个问题,使用AutoMapper(这是一个很好的技术)来映射一个业务对象到一个DTO的地方,我从集合中的抽象基类inheritance。
这是我的对象:
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不能创build。 如果我从支付对象中删除抽象关键字,那么代码就可以工作,但是我只能得到一个Payment对象的集合,我没有得到它们的具体对象(现金和信用卡支付)。
所以问题是:我怎样才能让AutoMapper映射特定的支付types,而不是基类?
更新
我做了一些更多的挖掘,并认为我看到一个问题,但不知道如何用AutoMapper解决这个问题。 我认为这是一个EF的东西,而不是AutoMapper的错。 🙂
在我的代码中,我使用entity framework4代理POCOs与延迟加载。
所以当我尝试映射从EF返回的实体是一个代理POCO它得到了这样的有趣的types:
System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2
所以我的理论是,当AutoMapper试图将CashPayment映射到DtoCashPayment,并且传入的付款是代理typesAutoMapper将其视为“不匹配”,然后映射通用付款types。 但是由于Payment是抽象类AutoMapper与“System.InvalidOperationException:无法创build抽象类的实例”的炸弹。 例外。
所以问题是:有没有办法让我使用AutoMapper将EF POCO代理对象映射到Dtos。
由于我刚刚面对与EF4 POCO代理相同的问题,这个答案有点晚了。
我使用调用Mapper.DynamicMap<TDestination>(object source)
的自定义转换器来解决此问题,以调用运行时types转换,而不是.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>();
我也试过奥利维尔的例子,并得到相同的StackOverflow错误。 我也尝试了subkamran的解决scheme,但没有运气,因为我没有使用实体模型代码生成的基类。 Automapper仍然爆炸。 在find更好的解决scheme之前,我只是在创buildContext对象时将Context设置为不创build代理。
model.Configuration.ProxyCreationEnabled = false; model.Configuration.LazyLoadingEnabled = true;
我也想看看这个问题的答案,也许使用一些东西内置到Automapper中…
更新:Automapper的预发布更正了这个问题,并允许映射覆盖DynamicProxy没有额外的configuration。
这个工作的版本是2.2.1
基于Olivier的回应,我无法让他在我的上下文中工作……它一直在无限循环中抛出一个StackOverflowException。
在这个例子中, AbstractClass
是我的基类, AbstractViewModel
是我的基本视图模型(不标记为abstract
头脑)。
但是,我确实得到它使用这个hackish转换器工作:
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
将正常映射,但是当代码检查它的基types( DerivedClassA
)时, DerivedClassA
也将正确映射。
请,请告诉我,我不必做这个疯狂的查找废话。 我不知道足够AutoMapper修复奥利维尔的答案。
我遇到了与entity framework代理相同的问题,但不想切换到AutoMapper的预发布版本。 我发现了一个简单的如果稍微丑陋的工作2.2.0版本。 我试图从一个DTO去现有的EF代理对象,并得到错误的丑陋代理类名称的映射。 我的解决scheme是使用指定的实际具体types,我手动映射的重载:
Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
我刚刚面临与dynamicEF代理映射到MVC应用程序中的ViewModels相同的问题。
我发现一个简单的解决scheme使用Mapper.DynamicMap()来解决这个问题。 这是我的代码:
从dynamic代理转换为ViewModel类:
// dynamic proxy instance WebService webService = _repWebService.GetAll().SingleOrDefault(x => x.Id == id); //mapping FirstStepWebServiceModel model = Mapper.DynamicMap<FirstStepWebServiceModel>(webService);
从ViewModel类转换为EFdynamic代理:
[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); }
希望这个例子帮助你