Automapper: проблема отображения с наследованием и абстрактный базовый класс в коллекциях с помощью Entity Framework 4 Proxy Pocos - PullRequest
39 голосов
/ 09 августа 2010

У меня проблема с использованием 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 кредитной картой), когда mapper пытается отобразить их, я получаю сообщение об ошибке, что абстрактный класс Payment не может быть создан.Если я удаляю абстрактное ключевое слово из объекта Payment, то код работает, но я получаю только коллекцию объектов Payment, я не получаю их конкретные объекты (платежи наличными и кредитной картой).

Поэтому вопрос таков:Как я могу заставить AutoMapper отображать определенные типы платежей, а не базовый класс?


Обновление

Я сделал еще несколько копаний и думаю, что вижу проблемуно я не уверен, как я могу решить эту проблему с AutoMapper.Я думаю, что это скорее вещь EF, а не вина AutoMapper.: -)

В моем коде я использую POCO-прокси Entity Framework 4 с отложенной загрузкой.

Так что, когда я пытаюсь отобразить сущность, возвращенную из EF, которая является POCO-прокси, это становится забавнымвыглядит как:

System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2

Так что моя теория заключается в том, что когда AutoMapper пытается сопоставить CashPayment с DtoCashPayment, а переданный платеж имеет тип прокси, AutoMapper видит его как «не совпадающий», а затем отображает общийСпособ оплаты.Но так как Payment является абстрактным классом, бомбы AutoMapper имеют «System.InvalidOperationException: экземпляры абстрактных классов не могут быть созданы».исключение.

Поэтому вопрос таков: есть ли способ использовать AutoMapper для сопоставления прокси-объектов EF POCO с Dtos.

Ответы [ 5 ]

15 голосов
/ 02 октября 2012

Я также попробовал пример Оливье и получил те же ошибки StackOverflow. Я также попробовал решение Субкамрана, но не повезло, так как я не использую базовый класс из генерации кода сущностной модели. Automapper все еще взрывается. Пока я не найду лучшего решения, я просто устанавливаю контекст, чтобы не создавать прокси, когда я создаю объект контекста.

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

Я также хотел бы увидеть ответ на проблему, возможно, используя что-то встроенное в Automapper ...

ОБНОВЛЕНИЕ: Предварительная версия Automapper исправляет эту проблему и позволяет отображению покрывать DynamicProxy без дополнительной настройки.

Релиз, в котором это работает - 2.2.1

15 голосов
/ 23 декабря 2010

Этот ответ приходит "немного" поздно, поскольку я только что столкнулся с той же проблемой с прокси-серверами EF4.

Я решил ее с помощью пользовательского конвертера, который вызывает 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>();
13 голосов
/ 22 апреля 2011

Опираясь на ответ Оливье, я не мог заставить его работать в моем контексте ... он продолжал бесконечный цикл и вызывал исключение 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, чтобы правильно исправить ответ Оливье.

10 голосов
/ 22 ноября 2012

Я столкнулся с той же проблемой с прокси Entity Framework, но не хотел переходить на предварительную версию AutoMapper. Я нашел простую, хотя и немного некрасивую работу для версии 2.2.0. Я пытался перейти от DTO к существующему прокси-объекту EF и получал ошибки об отсутствии отображения для уродливого имени прокси-класса. Мое решение состояло в том, чтобы использовать перегрузку, указав фактические конкретные типы, которые я бы отобразил вручную:

Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
6 голосов
/ 17 июня 2013

Я только что столкнулся с той же проблемой при отображении динамических прокси EF в ViewModels в приложении MVC.

Я нашел простое решение, используя Mapper.DynamicMap () для этой проблемы. Вот мой код:

Преобразование из динамического прокси в класс ViewModel:

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

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

Преобразование из класса ViewModel в EF Dynamic Proxy:

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

Надеюсь, этот пример поможет вам

...