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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3441916/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 14:35:44  来源:igfitidea点击:

Automapper : mapping issue with inheritance and abstract base class on collections with Entity Framework 4 Proxy Pocos

.netentity-frameworkcollectionspocoautomapper

提问by Ken Burkhardt

I am having an issue using AutoMapper (which is an excellent technology) to map a business object to a DTO where I have inheritance off of an abstract base class within a collection.

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

Here are my objects:

这是我的对象:

abstract class Payment
class CashPayment : Payment
class CreditCardPayment : Payment

I also have an invoice object which contains a collection of payments like so:

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

    public class Invoice
    {
       ... properties...

       public ICollection<Payment> Payments { get; set; }
    }

I also have corresponding DTO versions of each of these objects.

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

The DtoInvoice object is defined as:

DtoInvoice 对象定义为:

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

   [DataMember]
   public List<DtoPayment> Payments { get; set; }
}

This is what my Mapper definitions look like:

这是我的 Mapper 定义的样子:

Mapper.CreateMap<Invoice, DtoInvoice>();

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

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

The code to perform the mapping looks like this:

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

var invoice = repo.GetInvoice(invoiceId);

var dtoInvoice = Mapper.Map<Invoice, DtoInvoice>(invoice);

So for example if my invoice object contains a collection of specific payments (say 1 cash and 1 credit card) when mapper tries to map them I get an error that the abstract class Payment cannot be created. If I remove the abstract keyword from the Payment object then the code works but I only get a collection of Payment object, I do not get their specific objects (Cash & Credit Card payments).

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

So the question is: How can I get AutoMapper to map the specific payment types and not the base class?

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



Update

更新

I did some more digging and think I see a problem but am not sure how I can solve this with AutoMapper. I think this is more of an EF thing and not AutoMapper's fault. :-)

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

In my code I am using Entity Framework 4 Proxy POCOs with lazy loading.

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

So when I try to map an entity returned from EF that is a proxy POCO it gets that funny looking type like:

因此,当我尝试映射从代理 POCO 的 EF 返回的实体时,它会得到类似以下有趣的类型:

System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2

So my theory is that when AutoMapper tries to map CashPayment to DtoCashPayment and the payment passed in is of the proxy type AutoMapper sees it as a "non match" and then maps the generic Payment type. But since Payment is an abstract class AutoMapper bombs with a "System.InvalidOperationException: Instances of abstract classes cannot be created." exception.

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

So the question is: Is there a way for me to use AutoMapper to map EF POCO proxy objects to Dtos.

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

采纳答案by oli

This answer comes 'a bit' late as I've just faced the same issue with EF4 POCO proxies.

这个答案来得有点晚,因为我刚刚在 EF4 POCO 代理上遇到了同样的问题。

I solved it using a custom converter that calls Mapper.DynamicMap<TDestination>(object source)to invoke the runtime type conversion, rather than the .Include<TOtherSource, TOtherDestinatio>().

我使用自定义转换器解决了这个问题,该转换器调用Mapper.DynamicMap<TDestination>(object source)调用运行时类型转换,而不是.Include<TOtherSource, TOtherDestinatio>().

It works fine for me.

这对我来说可以。

In your case you would define the following converter:

在您的情况下,您将定义以下转换器:

class PaymentConverter : ITypeConverter<Payment, DtoPayment> {
    public DtoPayment Convert( ResolutionContext context ) {
        return Mapper.DynamicMap<DtoPayment>( context.SourceValue );
    }
}

And then:

进而:

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

回答by chrislhardin

I also tried Olivier's example and got the same StackOverflow errors. I also tried subkamran's solution but not luck there as I am not using a base class from the entity model code generation. Automapper still blows up. Until I find a better solution, I just set the Context to not create Proxies when I create a Context object.

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

model.Configuration.ProxyCreationEnabled = false; 
model.Configuration.LazyLoadingEnabled = true; 

I would also like to see an answer to the problem perhaps using something build into Automapper...

我也想看到这个问题的答案,也许使用 Automapper 内置的东西......

UPDATE: The Pre-release of Automapper corrects this issue and allows for the mapping to cover a DynamicProxy with no extra configuration.

更新:Automapper 的预发布版更正了这个问题,并允许映射无需额外配置即可覆盖 DynamicProxy。

The release this works in is 2.2.1

这个作品的版本是 2.2.1

回答by kamranicus

Building on Olivier's response, I could not get his to work in my context... it kept going in an infinite loop and threw a StackOverflowException.

基于 Olivier 的回应,我无法让他在我的上下文中工作......它一直在无限循环中并抛出 StackOverflowException。

In this example, AbstractClassis my base class and AbstractViewModelis my base view model (not marked as abstractmind you).

在这个例子中,AbstractClass是我的基类,AbstractViewModel是我的基本视图模型(没有标记为abstract介意你)。

However, I did get it to work using this hackish looking converter:

但是,我确实使用这个看起来很黑的转换器让它工作了:

    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>());

So, a DerivedClassAwill map normally, but a DynamicProxy_xxxwill also map properly as this code inspects its base type (DerivedClassA).

因此, aDerivedClassA将正常映射,但 aDynamicProxy_xxx也将正确映射,因为此代码检查其基本类型 ( DerivedClassA)。

Please, please, please show me that I don't have to do this crazy lookup crap. I don't know enough AutoMapper to fix Olivier's answer properly.

拜托,拜托,请告诉我,我不必做这种疯狂的查找废话。我不知道足够的 AutoMapper 来正确修复 Olivier 的答案。

回答by davesw

I ran into the same issue with Entity Framework proxies, but didn't want to switch to a pre-release version of AutoMapper. I found a simple if slightly ugly work around for version 2.2.0. I was trying to go from a DTO to an existing EF proxy object, and was getting errors about missing a mapping for the ugly proxy class name. My solution was to use an overload the specified the actual concrete types that I'd manually mapped:

我在使用 Entity Framework 代理时遇到了同样的问题,但不想切换到 AutoMapper 的预发布版本。我为 2.2.0 版找到了一个简单但略显丑陋的解决方法。我试图从一个 DTO 转到一个现有的 EF 代理对象,并且遇到了关于缺少丑陋代理类名的映射的错误。我的解决方案是使用指定的我手动映射的实际具体类型的重载:

Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));

回答by Ilya Schukin

I've just faced the same problem with mapping dynamic EF proxies to ViewModels in MVC application.

我刚刚在 MVC 应用程序中将动态 EF 代理映射到 ViewModel 时遇到了同样的问题。

I found an easy solution using Mapper.DynamicMap()for this problem. Here is my code:

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

Converting from Dynamic proxy to ViewModel class:

从动态代理转换为 ViewModel 类:

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

//mapping
FirstStepWebServiceModel model = Mapper.DynamicMap<FirstStepWebServiceModel>(webService);

Converting from ViewModel class to EF Dynamic Proxy:

从 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);
}

Hope this example help you

希望这个例子对你有帮助