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.
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.
The DtoInvoice object is defined as:
[DataContract]
public class DtoInvoice
{
...properties...
[DataMember]
public List<DtoPayment> Payments { get; set; }
}
This is what my Mapper definitions look like:
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).
So the question is: How can I get AutoMapper to map the specific payment types and not the base class?
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. :-)
In my code I am using Entity Framework 4 Proxy POCOs with lazy loading.
So when I try to map an entity returned from EF that is a proxy POCO it gets that funny looking type like:
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.
So the question is: Is there a way for me to use AutoMapper to map EF POCO proxy objects to Dtos.
This answer comes 'a bit' late as I've just faced the same issue with EF4 POCO proxies.
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>()
.
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>();
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.
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...
UPDATE: The Pre-release of Automapper corrects this issue and allows for the mapping to cover a DynamicProxy with no extra configuration.
The release this works in is 2.2.1
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.
In this example, AbstractClass
is my base class and AbstractViewModel
is my base view model (not marked as abstract
mind you).
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 DerivedClassA
will map normally, but a DynamicProxy_xxx
will also map properly as this code inspects its base type (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.
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:
Mapper.Map(dtoSource, entityDest, typeof(DtoClass), typeof(ConcreteEntityClass));
I've just faced the same problem with mapping dynamic EF proxies to ViewModels in MVC application.
I found an easy solution using Mapper.DynamicMap() for this problem. Here is my code:
Converting from Dynamic proxy to ViewModel class:
// 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:
[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