With the following example (LinqPad):
void Main()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, DestinationNested>()
.ConstructUsing((source, context) => new DestinationNested(source.InnerValue));
cfg.CreateMap<Source, DestinationOuter>()
.ForMember(x => x.OuterValue, y => y.MapFrom(z => z.OuterValue))
.ConstructUsing((source, context) =>
{
return new DestinationOuter(source.OuterValue, context.Mapper.Map<DestinationNested>(source));
});
});
var src = new Source { OuterValue = 999, InnerValue = 111 };
var mapper = config.CreateMapper();
var mapped = mapper.Map<DestinationOuter>(src);
mapped.Dump();
mapper.ConfigurationProvider.AssertConfigurationIsValid();
}
public class Source
{
public int OuterValue { get; set; }
public int InnerValue { get; set; }
}
public class DestinationOuter
{
public int OuterValue { get; private set; }
public DestinationNested destinationNested { get; private set; }
public DestinationOuter(int outerValue, DestinationNested destinationNested)
{
this.OuterValue = outerValue;
this.destinationNested = destinationNested;
}
}
public class DestinationNested
{
public int NestedValue { get; private set; }
public DestinationNested(int nestedValue)
{
this.NestedValue = nestedValue;
}
}
AssertConfigurationIsValid() currently throws an exception regarding the properties as I'm using ContructUsing.
In practice it does map correctly, but I'd like AssertConfigurationIsValid as part of my test suite to look for regressions (without needing to do manual tests of the mapper).
I'd like reassurance that all my properties are mapped from the source to the destination via the contructor. I wish to use a contructor as it's my Domain tier and the contructor enforces the mandatory items.
I don't wish to ignore all private setters via the IgnoreAllPropertiesWithAnInaccessibleSetter() feature, as I might be ignoring something which I haven't actually set.
Ideally I also don't want to need to do manual Ignore() on each of the properties which appear in the constructor as that leaves a gap for code drift.
I've tried various combinations in Automapper but no luck so far.
I suppose it's a static analysis challenge; I wish to know that my contructor covers all properties in the Destination. And I wish to know that the contructor is being passed everything from the source.
I realise that Automapper isn't doing very much automatic at this point, is there a nice way to lean on automapper for this testing or is this instead a static analysis problem?
Here's my take.
static void Main(string[] args)
{
try{
var mapperCfg = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, DestinationOuter>().ForCtorParam("destinationNested", o => o.MapFrom(s => new DestinationNested(s.InnerValue)));
});
mapperCfg.AssertConfigurationIsValid();
var mapper = mapperCfg.CreateMapper();
var src = new Source { OuterValue = 999, InnerValue = 111 };
mapper.Map<DestinationOuter>(src).Dump();
}catch(Exception ex){
ex.ToString().Dump();
}
}
public class Source
{
public int OuterValue { get; set; }
public int InnerValue { get; set; }
}
public class DestinationOuter
{
public int OuterValue { get; }
public DestinationNested DestinationNested { get; }
public DestinationOuter(int outerValue, DestinationNested destinationNested)
{
this.OuterValue = outerValue;
this.DestinationNested = destinationNested;
}
}
public class DestinationNested
{
public int NestedValue { get; private set; }
public DestinationNested(int nestedValue)
{
this.NestedValue = nestedValue;
}
}