3
votes

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

1
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.Ben Robinson
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)jmzagorski
How does the last line (the one using ConvertUsing) work if CreateCardVM doesn't inherit from AuditorVM?Andrew Whitaker
it works in the sense that it only maps the complex type AuditorVm on CreateCardVm to the base type properties on SetNewCardSettingsjmzagorski
Is there any way you can set up an example that shows the issue on a site like dotnetfiddle.net?Andrew Whitaker

1 Answers

4
votes

.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