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

jmzagorski
  • 1,135
  • 18
  • 42
  • 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 Dec 05 '14 at 14:21
  • 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 Dec 05 '14 at 14:34
  • How does the last line (the one using `ConvertUsing`) work if `CreateCardVM` doesn't inherit from `AuditorVM`? – Andrew Whitaker Dec 05 '14 at 14:40
  • it works in the sense that it only maps the complex type `AuditorVm` on `CreateCardVm` to the base type properties on `SetNewCardSettings` – jmzagorski Dec 05 '14 at 14:49
  • Is there any way you can set up an example that shows the issue on a site like http://dotnetfiddle.net? – Andrew Whitaker Dec 05 '14 at 14:55

1 Answers1

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

Community
  • 1
  • 1
Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
  • It worked! you just saved me a bunch of time, thanks! – jmzagorski Dec 05 '14 at 20:35
  • Would you know why another complex property on `CreateCardVm` whose properties are going straight to `SetNewCardSetting` are not mapping? I have updated the fiddle https://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 convention – jmzagorski Dec 08 '14 at 02:15
  • 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: https://dotnetfiddle.net/jOVaWl – Andrew Whitaker Dec 08 '14 at 13:56