Gonzalo Lorieto
Gonzalo Lorieto

Reputation: 645

AutoMapper advance mapping

I want to like merge those Source objects into a List<Destination>. Notice that SourceParent and Destination Id property MUST be the same.

var parent = new SourceParent
{
    Id = 1,
    Childs = new List<SourceChild>
     {
         new SourceChild { ChildId = 12, OtherProperty = "prop1" },
         new SourceChild { ChildId = 13, OtherProperty = "prop2" },
         new SourceChild { ChildId = 14, OtherProperty = "prop3" },
     }
};

Mapper.Initialize(cfb =>
{
    cfb.CreateMap<SourceParent, List<Destination>>()
    .ForMember(dest => dest, opt => opt.MapFrom(src => src.Childs));
    cfb.ValidateInlineMaps = false;
});

List<Destination> destination = Mapper.Map<SourceParent, List<Destination>>(parent);           

Classes:

public class SourceParent
{
    public int Id { get; set; }
    public List<SourceChild> Childs { get; set; }
}

public class SourceChild
{
    public string OtherProperty { get; set; }
    public int ChildId { get; set; }
}

public class Destination
{
    public int SourceParentId { get; set; }
    public string OtherProperty { get; set; }
    public int ChildId { get; set; }
}

Is there a way to create a mapping rule for this case? Is it even possible?

Upvotes: 0

Views: 1522

Answers (1)

Jamie Peacock
Jamie Peacock

Reputation: 372

I think your best option here is to define a TypeConverter.

You can do TypeConverters inline like I've done below or you can define a class that implements the ITypeConverter<TIn, TOut> interface.

cfb.CreateMap<SourceParent, List<Destination>>().ConvertUsing((src, dest, context) =>
{
    return src.Childs.Select(x => 
    {
        var destination = context.mapper.Map<Destination>(x);
        destination.SourceParentId = src.Id;
        return destination;
    }
});

If you wanted to (I usually stay away from this because it can get unruly fast) you could define another custom mapping using a tuple or a wrapper class like this.

cfb.CreateMap<SourceParent, List<Destination>>().ConvertUsing((src, dest, context) =>
{
    return src.Childs.Select(x => context.mapper.Map<Destination>((src.Id, x)))
});

cfb.CreateMap<(int partentId, SourceChild child), Destination>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.parentId))
.ForMember(dest => dest.ChildId, opt => opt.MapFrom(src => src.child.Id))
.ForMember(dest => dest.OtherProperty , opt => opt.MapFrom(src => src.child.OtherProperty ));

This can be nice for small examples but if you are doing it often it can lead to really cluttered mapper configurations (in my opinion), but it does simplify your type converter.

Upvotes: 1

Related Questions