Automapper: проблема сопоставления с наследованием и абстрактным базовым классом в коллекциях с прокси-Pocos Entity Framework 4
у меня возникла проблема с использованием AutoMapper (это отличная технология) для сопоставления бизнес-объекта с DTO, где у меня есть наследование абстрактного базового класса в коллекции.
вот мои объекты:
abstract class Payment
class CashPayment : Payment
class CreditCardPayment : Payment
у меня также есть объект счета, который содержит коллекцию платежей, например:
public class Invoice
{
... properties...
public ICollection<Payment> Payments { get; set; }
}
у меня также есть соответствующие версии DTO каждого из этих объектов.
определен объект DtoInvoice as:
[DataContract]
public class DtoInvoice
{
...properties...
[DataMember]
public List<DtoPayment> Payments { get; set; }
}
вот как выглядят мои определения картографа:
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 пытается сопоставить их, я получаю ошибку, что абстрактный класс оплаты не может быть создан. Если я удаляю ключевое слово abstract из объекта Payment, код работает, но я получаю только коллекцию Объект оплаты, я не получаю их конкретные объекты (наличные и платежи по кредитным картам).
Итак, вопрос: Как я могу получить AutoMapper для сопоставления конкретных типов платежей, а не базового класса?
обновление
Я сделал еще несколько раскопок и думаю, что вижу проблему, но не уверен, как я могу решить это с помощью AutoMapper. Я думаю, что это скорее вещь EF, а не вина AutoMapper. :-)
в моем коде я использую Entity Framework 4 Прокси POCOs с ленивой загрузкой.
поэтому, когда я пытаюсь сопоставить объект, возвращенный из EF, который является прокси-POCO, он получает такой забавный тип, как:
System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2
поэтому моя теория заключается в том, что, когда AutoMapper пытается сопоставить CashPayment с DtoCashPayment, а переданный платеж имеет тип прокси-сервера, AutoMapper видит его как "не совпадающий", а затем сопоставляет общий тип платежа. Но так как оплата является абстрактным классом AutoMapper бомб с "системой".Исключение InvalidOperationException: Экземпляры абстрактных классов не могут быть созданы." исключение.
Итак, вопрос: есть ли способ использовать AutoMapper для сопоставления прокси-объектов EF POCO с Dtos.
5 ответов
этот ответ приходит "немного" поздно, так как я только что столкнулся с той же проблемой с прокси-серверами 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>();
Я также попробовал пример Оливье и получил те же ошибки StackOverflow. Я также попробовал решение subkamran, но не повезло, так как я не использую базовый класс из генерации кода модели сущности. Automapper все еще взрывается. Пока я не найду лучшее решение, я просто устанавливаю контекст, чтобы не создавать прокси при создании объекта контекста.
model.Configuration.ProxyCreationEnabled = false;
model.Configuration.LazyLoadingEnabled = true;
Я также хотел бы увидеть ответ на проблему, возможно, используя что-то встроенное в Automapper...
обновление: Предварительный выпуск Automapper исправляет эту проблему и позволяет сопоставлению покрывать DynamicProxy без дополнительной конфигурации.
релиз это работает в 2.2.1
основываясь на ответе Оливье, я не мог заставить его работать в моем контексте... он продолжал идти в бесконечном цикле и бросил 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>());
Итак, a DerivedClassA
будет отображаться нормально, но DynamicProxy_xxx
также будет правильно отображаться, поскольку этот код проверяет его базовый тип (DerivedClassA
).
пожалуйста, пожалуйста, пожалуйста, покажите мне, что я не должен делать это сумасшедшее дерьмо поиска. Я не знаю достаточно AutoMapper, чтобы исправить ответ Оливье должным образом.
я столкнулся с той же проблемой с прокси-серверами Entity Framework, но не хотел переключаться на предварительную версию AutoMapper. Я нашел простую, хотя и немного уродливую работу для версии 2.2.0. Я пытался перейти от DTO к существующему объекту прокси EF и получал ошибки об отсутствии сопоставления для уродливого имени прокси-класса. Мое решение состояло в том, чтобы использовать перегрузку указанных фактических конкретных типов, которые я вручную сопоставил:
Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
Я только что столкнулся с той же проблемой с отображением динамических прокси EF для ViewModels в приложении MVC.
Я нашел простое решение, используя маппер.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);
}
надеюсь, этот пример поможет вам