How to convert complex nested object hierarchy using Automapper

1k Views Asked by At

I have these following class under two different namespaces, e.g. Source and Target.

Mapping classes:

public class Instance
{
    public Type Type { get; set; }
    public object Definition { get; set; }
}

public sealed class Class : Instance
{
    private IList<Property> m_Properties;
    public IList<Property> Properties
    {
        get { return m_Properties ?? (m_Properties = new List<Property>()); }
    }
}

public abstract class Member : Instance
{
    public string Name { get; set; }
}

public sealed class Parameter : Member
{
}

public sealed class Property : Member
{
}

Note that the Instance class has Definition property of type object that will hold the Class reference so the nested hierarchy will start from here up to N levels. I was converting fine but the Definition property inside Class and its nested hierarchy objects were still holding Source reference instead of Target. After the addition of ForMember(t => t.Definition, opt => opt.MapFrom(s => Mapper.Map<Source.Class, Target.Class>((Source.Class)s.Definition))) to make this transformation work somehow, it has started giving exception.

Usage:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Source.Member, Target.Member>()
                .Include<Source.Property, Target.Property>()
                .Include<Source.Parameter, Target.Parameter>()
                .ForMember(t => t.Definition, opt => opt.MapFrom(s => Mapper.Map<Source.Class, Target.Class>((Source.Class)s.Definition)));

    cfg.CreateMap<Source.Property, Target.Property>();
    cfg.CreateMap<Source.Parameter, Target.Parameter>();
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();

var definitionLevel1 = new Source.Class();
definitionLevel1.Properties.Add(new Source.Property() { Name = "PropertyLevel_1.1" });
definitionLevel1.Properties.Add(new Source.Property() { Name = "PropertyLevel_1.2" });

var definitionLevel2 = new Source.Class();
definitionLevel2.Properties.Add(new Source.Property() { Name = "PropertyLevel_2.1" });
definitionLevel1.Definition = definitionLevel2;

Source.Member sourceMember = new Source.Property()
{
    Name = "Some_Property_Name",
    Definition = definitionLevel1,
    Type = typeof(CompositeType)
};

IEnumerable<Source.Member> sourceMembers = new List<Source.Member>() { sourceMember };
var targetMembers = mapper.Map<IEnumerable<Target.Member>>(sourceMembers);

Please assist me filling the gaps or missing pieces.

1

There are 1 best solutions below

1
On

Ahhh finally, i have solved this complex problem, so far the results seems fine but suggestions to use even better practice (if any) are welcomed as i am a beginner and just started using Automapper.

So following are the configurations with respective mappers.

Configuration:

IMapper memberMapper = null;
IMapper classMapper = null;

var classConfig = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Source.Class, Target.Class>()
        .ForMember(t => t.Properties, opt => opt.MapFrom(s => memberMapper.Map<IList<Target.Property>>(s.Properties)))
        .ForMember(t => t.Definition, opt => opt.MapFrom(s => classMapper.Map<Source.Class, Target.Class>((Source.Class)s.Definition)));
    cfg.CreateMap<Source.Property, Target.Property>();
});
classConfig.AssertConfigurationIsValid();
classMapper = classConfig.CreateMapper();

var memberConfig = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Source.Member, Target.Member>()
        .Include<Source.Property, Target.Property>()
        .Include<Source.Parameter, Target.Parameter>()
        .ForMember(t => t.Definition, opt => opt.MapFrom(s => classMapper.Map<Source.Class, Target.Class>((Source.Class)s.Definition)));

    cfg.CreateMap<Source.Property, Target.Property>();
    cfg.CreateMap<Source.Parameter, Target.Parameter>();
});
memberConfig.AssertConfigurationIsValid();
memberMapper = memberConfig.CreateMapper();

The idea that i have used is that the two mappers are working together as per their scope to get this mapping done recursively.