Automapper map one property to multiple

10.6k views Asked by At

I am facing a challenge with AutoMapper between my source object and destination object. I will try to explain the sittuation. On my src object I have an string that according to its lenght it should be mapped to multiple properties of my destination 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 max length expected is 30 chars (It can be less than that which will be mapped to only two properties or one). So every 10 will be mapped to each destination property. I was trying to use the ResolveUsing method from AutoMapper but there is no way to let the function to know which segment I should bring back. So I was thinking to ignore the mapping of this properties and do this manually after Automapper has dones its job with other properties

2

There are 2 answers

1
Andrew Whitaker On BEST ANSWER

On way you could do this is to use .ConstructUsing to tell AutoMapper how to create the object You could create a function that maps Value1, Value2, Value3 manually, then letting AutoMapper map 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 Value fields, this could become nasty--in that case you could use reflection to set the properties.

3
soulus On

You can create a mapping function using the ForMember specification

I.e.

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 just evaluate on every mapping that the original string contains the appropiate lenght and otherwise return string.Empty or stuff it to suit 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();
}