Automapper:使用Entity Framework 4 Proxy Pocos在集合上映射继承和抽象基类的问题

Ken*_*rdt 39 .net collections entity-framework poco automapper

我在使用AutoMapper(这是一项出色的技术)时遇到了一个问题,即将业务对象映射到DTO,在DTO中我从集合中的抽象基类继承.

这是我的对象:

abstract class Payment
class CashPayment : Payment
class CreditCardPayment : Payment
Run Code Online (Sandbox Code Playgroud)

我还有一个发票对象,其中包含一系列付款,如下所示:

    public class Invoice
    {
       ... properties...

       public ICollection<Payment> Payments { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

我还有每个这些对象的相应DTO版本.

DtoInvoice对象定义为:

[DataContract]
public class DtoInvoice
{
   ...properties...

   [DataMember]
   public List<DtoPayment> Payments { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这就是我的Mapper定义:

Mapper.CreateMap<Invoice, DtoInvoice>();

Mapper.CreateMap<Payment, DtoPayment>()
  .Include<CashPayment, DtoCashPayment>()
  .Include<CreditCardPayment, DtoCreditCardPayment>();

Mapper.CreateMap<CashPayment, DtoCashPayment>();
Mapper.CreateMap<CreditCardPayment, DtoCreditCardPayment>();
Run Code Online (Sandbox Code Playgroud)

执行映射的代码如下所示:

var invoice = repo.GetInvoice(invoiceId);

var dtoInvoice = Mapper.Map<Invoice, DtoInvoice>(invoice);
Run Code Online (Sandbox Code Playgroud)

因此,例如,如果我的发票对象包含特定付款的集合(比如1现金和1张信用卡),当映射器尝试映射它们时,我会收到错误,即无法创建抽象类付款.如果我从Payment对象中删除了abstract关键字,那么代码可以工作,但我只收到Payment对象的集合,我没有得到他们的特定对象(现金和信用卡付款).

所以问题是:如何让AutoMapper映射特定的支付类型而不是基类?


更新

我做了一些挖掘,并认为我发现了一个问题,但我不确定如何使用AutoMapper解决这个问题.我认为这更像是EF的事情,而不是AutoMapper的错.:-)

在我的代码中,我使用延迟加载的Entity Framework 4代理POCO.

因此,当我尝试映射从EF返回的实体(代理POCO)时,它会获得如下滑稽的类型:

System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2
Run Code Online (Sandbox Code Playgroud)

所以我的理论是,当AutoMapper尝试将CashPayment映射到DtoCashPayment并且传入的付款属于代理类型时,AutoMapper将其视为"不匹配",然后映射通用付款类型.但由于Payment是一个抽象类AutoMapper炸弹,其中包含"System.InvalidOperationException:无法创建抽象类的实例".例外.

所以问题是:我有没有办法使用AutoMapper将EF POCO代理对象映射到Dtos.

oli*_*oli 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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

Mapper.CreateMap<Payment, DtoPayment>().ConvertUsing<PaymentConverter>();
Mapper.CreateMap<CashPayment, DtoCashPayment>();
Mapper.CreateMap<CreditCardPayment, DtoCreditCardPayment>();
Run Code Online (Sandbox Code Playgroud)

  • 我试过这个,我得到一个StackOverflowException :( (10认同)
  • 我使用Mapper.CreateMap <...>创建了地图,并使用Mapper.DynamicMap <...>()来执行映射,并且它有效.我没有必要创建自定义解析器. (3认同)
  • *bump*有没有人发现如何防止stackoverflowexception? (2认同)

chr*_*din 15

我也试过Olivier的例子并得到了相同的StackOverflow错误.我也试过subkamran的解决方案,但不是运气,因为我没有使用实体模型代码生成的基类.Automapper仍在爆炸.在找到更好的解决方案之前,我只是在创建Context对象时将Context设置为不创建Proxies.

model.Configuration.ProxyCreationEnabled = false; 
model.Configuration.LazyLoadingEnabled = true; 
Run Code Online (Sandbox Code Playgroud)

我也希望看到问题的答案,或许使用构建到Automapper中的东西......

更新:Automapper的预发布更正了此问题,并允许映射覆盖DynamicProxy而无需额外配置.

这个版本适用于2.2.1


kam*_*cus 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>());
Run Code Online (Sandbox Code Playgroud)

因此,a DerivedClassA会正常映射,但是DynamicProxy_xxx当这段代码检查其基类型(DerivedClassA)时,它也会正确映射.

请,请告诉我,我没有做这个疯狂的查找垃圾.我不知道足够的AutoMapper来正确修复Olivier的答案.


dav*_*esw 10

我遇到了与Entity Framework代理相同的问题,但不想切换到AutoMapper的预发布版本.对于2.2.0版本,我发现了一个简单但有点难看的工作.我试图从DTO转到现有的EF代理对象,并且错过了丢失丑陋的代理类名称的映射.我的解决方案是使用指定我手动映射的实际具体类型的重载:

Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
Run Code Online (Sandbox Code Playgroud)


小智 6

我刚刚遇到了将动态EF代理映射到MVC应用程序中的ViewModels时遇到的相同问题.

我找到了一个使用Mapper.DynamicMap()解决这个问题的简单方法.这是我的代码:

从动态代理转换为ViewModel类:

// dynamic proxy instance
WebService webService = _repWebService.GetAll().SingleOrDefault(x => x.Id == id);

//mapping
FirstStepWebServiceModel model = Mapper.DynamicMap<FirstStepWebServiceModel>(webService);
Run Code Online (Sandbox Code Playgroud)

从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);
}
Run Code Online (Sandbox Code Playgroud)

希望这个例子可以帮到你