.net - lazy - automapper net standard
Automapper: problema de asignaciĆ³n con herencia y clase base abstracta en colecciones con Entity Framework 4 Proxy Pocos (5)
Tengo un problema al usar AutoMapper (que es una tecnología excelente) para asignar un objeto comercial a un DTO donde tengo herencia de una clase base abstracta dentro de una colección.
Aquí están mis objetos:
abstract class Payment
class CashPayment : Payment
class CreditCardPayment : Payment
También tengo un objeto de factura que contiene una colección de pagos como esta:
public class Invoice
{
... properties...
public ICollection<Payment> Payments { get; set; }
}
También tengo las correspondientes versiones DTO de cada uno de estos objetos.
El objeto DtoInvoice se define como:
[DataContract]
public class DtoInvoice
{
...properties...
[DataMember]
public List<DtoPayment> Payments { get; set; }
}
Así son mis definiciones de Mapper:
Mapper.CreateMap<Invoice, DtoInvoice>();
Mapper.CreateMap<Payment, DtoPayment>()
.Include<CashPayment, DtoCashPayment>()
.Include<CreditCardPayment, DtoCreditCardPayment>();
Mapper.CreateMap<CashPayment, DtoCashPayment>();
Mapper.CreateMap<CreditCardPayment, DtoCreditCardPayment>();
El código para realizar la asignación se ve así:
var invoice = repo.GetInvoice(invoiceId);
var dtoInvoice = Mapper.Map<Invoice, DtoInvoice>(invoice);
Entonces, por ejemplo, si el objeto de mi factura contiene una colección de pagos específicos (por ejemplo, 1 efectivo y 1 tarjeta de crédito) cuando el asignador intenta mapearlos, aparece un error que indica que no se puede crear el pago de la clase abstracta. Si elimino la palabra clave abstracta del objeto Pago, entonces el código funciona pero solo obtengo una colección del objeto Pago, no obtengo sus objetos específicos (pagos en efectivo y con tarjeta de crédito).
Entonces, la pregunta es: ¿cómo puedo hacer que AutoMapper trace los tipos de pago específicos y no la clase base?
Actualizar
Hice un poco más de excavación y creo que veo un problema, pero no estoy seguro de cómo puedo resolverlo con AutoMapper. Creo que esto es más una cuestión de EF y no culpa de AutoMapper. :-)
En mi código, estoy utilizando Entity Framework 4 Proxy POCOs con carga lenta.
Entonces, cuando trato de mapear una entidad devuelta desde EF que es un POCO proxy, obtiene ese tipo de aspecto divertido como:
System.Data.Entity.DynamicProxies.CashPayment_86783D165755C316A2F58A4343EEC4842907C5539AF24F0E64AEF498B15105C2
Entonces mi teoría es que cuando AutoMapper intenta asignar CashPayment a DtoCashPayment y el pago pasado es de tipo proxy, AutoMapper lo ve como "no coincidente" y luego asigna el tipo de pago genérico. Pero dado que Payment es una clase abstracta, AutoMapper bombs con una "System.InvalidOperationException: no se pueden crear instancias de clases abstractas". excepción.
Entonces, la pregunta es: ¿hay alguna manera de usar AutoMapper para mapear objetos proxy de EF POCO a Dtos?
Acabo de enfrentar el mismo problema con el mapeo de proxies dinámicos de EF a ViewModels en la aplicación MVC.
Encontré una solución fácil usando Mapper.DynamicMap () para este problema. Aquí está mi código:
Conversión del proxy dinámico a la clase ViewModel:
// dynamic proxy instance
WebService webService = _repWebService.GetAll().SingleOrDefault(x => x.Id == id);
//mapping
FirstStepWebServiceModel model = Mapper.DynamicMap<FirstStepWebServiceModel>(webService);
Conversión de la clase ViewModel a 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);
}
Espero que este ejemplo te ayude
Esta respuesta llega un poco tarde, ya que acabo de enfrentar el mismo problema con los proxies POCO de EF4.
Lo resolví utilizando un convertidor personalizado que llama a Mapper.DynamicMap<TDestination>(object source)
para invocar la conversión de tipo de tiempo de ejecución, en lugar de .Include<TOtherSource, TOtherDestinatio>()
.
Funciona bien para mí.
En tu caso definirías el siguiente convertidor:
class PaymentConverter : ITypeConverter<Payment, DtoPayment> {
public DtoPayment Convert( ResolutionContext context ) {
return Mapper.DynamicMap<DtoPayment>( context.SourceValue );
}
}
Y entonces:
Mapper.CreateMap<Payment, DtoPayment>().ConvertUsing<PaymentConverter>();
Mapper.CreateMap<CashPayment, DtoCashPayment>();
Mapper.CreateMap<CreditCardPayment, DtoCreditCardPayment>();
Me encontré con el mismo problema con los proxies de Entity Framework, pero no quise cambiar a una versión preliminar de AutoMapper. Encontré un trabajo simple pero un poco feo para la versión 2.2.0. Estaba tratando de pasar de un DTO a un objeto proxy EF existente, y estaba obteniendo errores sobre la falta de una asignación para el nombre feo de la clase proxy. Mi solución fue usar una sobrecarga especificando los tipos de hormigón reales que había mapeado manualmente:
Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
Sobre la base de la respuesta de Olivier, no pude hacer que la suya funcionara en mi contexto ... continuó en un ciclo infinito y arrojó una Exception.
En este ejemplo, AbstractClass
es mi clase base y AbstractViewModel
es mi modelo de vista base (no está marcado como mente abstract
).
Sin embargo, lo hice funcionar con este convertidor de aspecto 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>());
Por lo tanto, un DerivedClassA
se correlacionará normalmente, pero un DynamicProxy_xxx
también se correlacionará correctamente cuando este código inspeccione su tipo base ( DerivedClassA
).
Por favor, por favor, muéstrame que no tengo que hacer esta loca búsqueda. No conozco suficiente AutoMapper para corregir la respuesta de Olivier correctamente.
También probé el ejemplo de Olivier y obtuve los mismos errores de . También probé la solución de Subkamran pero no tuve suerte ya que no estoy usando una clase base de la generación del código del modelo de entidad. Automapper todavía explota. Hasta que encuentre una solución mejor, simplemente configuro Contexto para no crear Proxies cuando creo un objeto Contexto.
model.Configuration.ProxyCreationEnabled = false;
model.Configuration.LazyLoadingEnabled = true;
También me gustaría ver una respuesta al problema quizás usando algo incorporado en Automapper ...
ACTUALIZACIÓN: El prelanzamiento de Automapper corrige este problema y permite que la asignación cubra un DynamicProxy sin configuración adicional.
El lanzamiento en el que esto funciona es 2.2.1