Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automapper map one property to multiple

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

like image 526
JAVH Avatar asked Nov 13 '14 19:11

JAVH


People also ask

Is AutoMapper faster than manual mapping?

Automapper is considerably faster when mapping a List<T> of objects on . NET Core (It's still slower on full . NET Framework).

Can AutoMapper map collections?

AutoMapper supports polymorphic arrays and collections, such that derived source/destination types are used if found.

What can I use instead of AutoMapper?

Mapster is an emerging alternative to AutoMapper which was first published in 2015 and has over 7.4 million NuGet package downloads.


2 Answers

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.

like image 107
Andrew Whitaker Avatar answered Sep 18 '22 16:09

Andrew Whitaker


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();
}
like image 27
soulus Avatar answered Sep 21 '22 16:09

soulus