Automapper: How to not repeat mapping config from complex type to base class
Asked Answered
C

1

3

I have a bunch of DTO classes that inherit from this CardBase:

// base class
public class CardBase
{
  public int TransId {get; set; }
  public string UserId  { get; set; }
  public int Shift { get; set; }
}

// one of the concrete classes
public class SetNewCardSettings : CardBase
{
  // specific properties ...
}

In my MVC project I have a bunch of view models with a AuditVm complex type that has the same properties of CardBase:

public class AuditVm
{
  public int TransId {get; set; }
  public string UserId  { get; set; }
  public int Shift { get; set; }
}

public class CreateCardVm : CardVm
{
  // specific properties here ...

  public AuditVm Audit { get; set }
}

Those view models cannot inherit from AuditVm because each of them already has a parent. I thought I could setup my mapping like below so I would not have to specify the map from AuditVm to the CardBase for every view model that has AuditVm as a complex type. But it is not working. How do I properly map from a complex type to a flatten type with properties on the base class?

  Mapper.CreateMap<AuditorVm, CardBase>()
    .Include<AuditorVm, SetNewCardSettings>();

  // this does not work because it ignores my properties that I map in the second mapping
  // if I delete the ignore it says my config is not valid
  Mapper.CreateMap<AuditorVm, SetNewCardSettings>()
    .ForMember(dest => dest.Temp, opt => opt.Ignore())
    .ForMember(dest => dest.Time, opt => opt.Ignore());

  Mapper.CreateMap<CreateCardVm, SetNewCardSettings>()
     // this gives me an error
    .ForMember(dest => dest, opt => opt.MapFrom(src => Mapper.Map<AuditorVm, SetNewCardSettings>(src.Auditor)));

    // I also tried this and it works, but it does not map my specific properties on SetNewCardSettings
    //.ConvertUsing(dest => Mapper.Map<AuditorVm, SetNewCardSettings>(dest.Auditor));

UPDATE: here is the fiddle https://dotnetfiddle.net/iccpE0

Cursed answered 5/12, 2014 at 14:9 Comment(5)
CAn you supply an example of a type that you would map to from CreateCardVm such that you would have to map Audit to a property of type CardBase.Psoas
ooh sorry my last mapping had wrong classes when I copied it over. The last mapping is from CreateCardVm (has AuditVm) to SetNewCardSettings (has CardBase)Cursed
How does the last line (the one using ConvertUsing) work if CreateCardVM doesn't inherit from AuditorVM?Hall
it works in the sense that it only maps the complex type AuditorVm on CreateCardVm to the base type properties on SetNewCardSettingsCursed
Is there any way you can set up an example that shows the issue on a site like dotnetfiddle.net?Hall
H
4

.Include is for a very specific case--you have two identically-structured class hierarchies you'd like to map, for example:

public class AEntity : Entity { }

public class BEntity : Entity { }

public class AViewModel : ViewModel { }

public class BViewModel : ViewModel { }

Mapper.CreateMap<Entity, ViewModel>()
    .Include<AEntity, AViewModel>()
    .Include<BEntity, BViewModel>();

// Then map AEntity and BEntity as well.

So unless you have this kind of situation, .Include isn't the right thing to use.

I think your best bet is to use ConstructUsing:

 Mapper.CreateMap<AuditVm, CardBase>();

 Mapper.CreateMap<AuditVm, SetNewCardSettings>()
     .ConstructUsing(src => 
          {
              SetNewCardSettings settings = new SetNewCardSettings();
              Mapper.Map<AuditVm, CardBase>(src, settings);
              return settings;
          })
     .IgnoreUnmappedProperties();

 Mapper.CreateMap<CreateCardVm, SetNewCardSettings>()
     .ConstructUsing(src => Mapper.Map<SetNewCardSettings>(src.Audit))
     .IgnoreUnmappedProperties();

I've also incorporated this answer's extension method to ignore all unmapped properties. Since we're using ConstructUsing, AutoMapper doesn't know that we've already taken care of those properties.

Updated fiddle: https://dotnetfiddle.net/6ZfZ3z

Hall answered 5/12, 2014 at 14:36 Comment(3)
It worked! you just saved me a bunch of time, thanks!Cursed
Would you know why another complex property on CreateCardVm whose properties are going straight to SetNewCardSetting are not mapping? I have updated the fiddle dotnetfiddle.net/jU7gto. The added class is Foo and the property is Bar and I added a CreateMap. Is this something that I have to explicitly map or change the property name to FooBar to map by conventionCursed
You could either create a map from Foo to string and use that, or use MapFrom(src => src.Foo == null ? null : Foo.Bar). Here's an updated fiddle: dotnetfiddle.net/jOVaWlHall

© 2022 - 2024 — McMap. All rights reserved.