Automapper maps one property to several
I am facing AutoMapper issue between my source and destination. I will try to explain the meaning. On my src object, I have a string that, according to its length, needs to be mapped to several properties of my target object.
class source
{
public int Id {get; set;}
/* some other properties */
public string Value {get; set;}
}
class destination
{
public int Id {get; set;}
/* some other properties with the same name as the source */
public string Value1 {get; set;}
public string Value2 {get; set;}
public string Value3 {get; set;}
}
The expected maximum length is 30 characters (it may be less than what will be displayed for only two properties or one). Thus, every 10 will be mapped to each destination property. I tried to use the ResolveUsing method from AutoMapper, but there is no way to tell the function which segment I need to return. So I thought to ignore the mapping of these properties and do it manually after Automapper did its job with other properties
source to share
In this case, you can use .ConstructUsing
to tell AutoMapper how to create the object. You can create a function that displays Value1
, Value2
, Value3
hand, and then allows AutoMapper display the remaining properties, for example:
static destination ConstructDestination(source src)
{
List<string> chunked = src.Value
.Select((ch, index) => new { Character = ch, Index = index })
.GroupBy(
grp => grp.Index / 10,
(key, grp) => new string(grp.Select(itm => itm.Character).ToArray()))
.ToList();
var dest = new destination
{
Value1 = chunked.Count > 0 ? chunked[0] : null,
Value2 = chunked.Count > 1 ? chunked[1] : null,
Value3 = chunked.Count > 2 ? chunked[2] : null
};
return dest;
}
Mapper.CreateMap<source, destination>()
.ConstructUsing(ConstructDestination)
.ForMember(dest => dest.Value1, opt => opt.Ignore())
.ForMember(dest => dest.Value2, opt => opt.Ignore())
.ForMember(dest => dest.Value3, opt => opt.Ignore());
/* Id is mapped automatically. */
Of course, if in your actual scenario you have more than three fields Value
this can get frustrating - in which case you can use reflection to set properties.
source to share
You can create a mapping function using the ForMember spec
those.
private void CreateMap(){
Mapper.CreateMap<source,destination>()
.ForMember(v=> v.Value1,
opts => opts.MapFrom( src=> src.Value.Substring(0,10)))
.ForMember(v=> v.Value2,
opts => opts.MapFrom( src=> src.Value.Substring(10,10)))
.ForMember(v=> v.Value3,
opts => opts.MapFrom( src=> src.Value.Substring(20,10)));
}
You can simply evaluate on each collation that the original string contains a suitable length and returns a string otherwise. Or use it according to your needs.
Usage (using LinqPad):
void Main(){
CreateMap();
var source = new source();
source.Id=1;
source.Value="123454678901234546789012345467";
var res = Mapper.Map<destination>(source);
res.Dump();
}
source to share