Click here to Skip to main content
15,744,397 members
Articles / Web Development / ASP.NET
Tip/Trick
Posted 26 Jul 2022

Stats

39.2K views
174 downloads
14 bookmarked

Map a Complex Object to a List of Objects using AutoMapper

Rate me:
Please Sign up or sign in to vote.
5.00/5 (8 votes)
26 Jul 2022CPOL1 min read
Conversion of complex object to a list of objects
In this post, we will focus on the conversion of a complex object to a list of objects.

Background

AutoMapper is a simple library that helps us to transform one object type to another. It is a convention based object to object mapper, that requires minimal configuration. Here, we will focus on the conversion of a complex object to a list of objects.

Installation & Configuration

To use AutoMapper in the web application, you need to install packages from NuGet.

Install-Package AutoMapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection

Add an AutoMapper profile class to the project. This profile class will hold mapping rules.

C#
public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
       /*mapping rules here*/
    }
}

Inside ConfigureServices(IServiceCollection services) of Startup.cs, add:

C#
/*Automapper*/
services.AddAutoMapper(typeof(Startup));

This will load all AutoMapper profiles from the project assembly.

Object to List

Here, we will convert a complex Team model to IEnumerable<TeamMember>. Team has nested List<People> property and other properties. TeamMember is a plain POCO model.

Model

Source Model
C#
public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<People> Members { get; set; }
}

public class People
{
    public int Id { get; set; }
    public string Name { get; set; }
}
Destination Model
C#
public class TeamMember
{
    public int TeamId { get; set; }
    public string TeamName { get; set; }
    public int PeopleId { get; set; }
    public string PeopleName { get; set; }
}

Map Settings

We need to place these mapping rules inside the AutoMapper profiles constructor.

C#
CreateMap<Team, TeamMember>()
    .ForMember(d => d.TeamId, opt => opt.MapFrom(s => s.Id))
    .ForMember(d => d.TeamName, opt => opt.MapFrom(s => s.Name));
CreateMap<People, TeamMember>()
    .ForMember(d => d.PeopleId, opt => opt.MapFrom(s => s.Id))
    .ForMember(d => d.PeopleName, opt => opt.MapFrom(s => s.Name));
CreateMap<Team, IEnumerable<TeamMember>>()
    .ConvertUsing<TeanToTeamMemberListConverter>();
  • CreateMap<Team, TeamMember>() convets Team to TeamMember
  • CreateMap<People, TeamMember>() converts People to TeamMember
  • CreateMap<Team, IEnumerable<TeamMember>>() converts Team to a list of TeamMember using a custom converter
Custom Converter
C#
public class TeanToTeamMemberListConverter : 
             ITypeConverter<Team, IEnumerable<TeamMember>>
{
    public IEnumerable<TeamMember> Convert
    (Team source, IEnumerable<TeamMember> destination, ResolutionContext context)
    {
        /*first mapp from People, then from Team*/
        foreach (var model in source.Members.Select
                (e => context.Mapper.Map<TeamMember>(e)))
        {
            context.Mapper.Map(source, model);
            yield return model;
        }

        /*first mapp from Team, then from People*/
        //foreach (var member in source.Members)
        //{
        //    var model = context.Mapper.Map<TeamMember>(source);
        //    context.Mapper.Map(member, model);
        //    yield return model;
        //}
    }
}

The converter is using previously configured Team to TeamMember and People to TeamMember mappings.

Using Mapper

C#
[HttpPost("[action]")]
public IEnumerable<TeamMember> ComplexObjectToList([FromBody] Team team)
{
    return Mapper.Map<IEnumerable<TeamMember>>(team);
}

List to Object

Here, we will convert a IEnumerable<TeamMember> to IEnumerable<Team>. TeamMember is a plain POCO model. Team is a complex model, which contains nested List<People> property and other properties. 

Model

Source Model
C#
public class TeamMember
{
    public int TeamId { get; set; }
    public string TeamName { get; set; }
    public int PeopleId { get; set; }
    public string PeopleName { get; set; }
}
Destination Object
C#
public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<People> Members { get; set; }
}

public class People
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Mapp Settings

C#
CreateMap<TeamMember, Team>()
    .ForMember(d => d.Id, opt => opt.MapFrom(s => s.TeamId))
    .ForMember(d => d.Name, opt => opt.MapFrom(s => s.TeamName));
CreateMap<TeamMember, People>()
    .ForMember(d => d.Id, opt => opt.MapFrom(s => s.PeopleId))
    .ForMember(d => d.Name, opt => opt.MapFrom(s => s.PeopleName));
CreateMap<IEnumerable<TeamMember>, IEnumerable<Team>>()
    .ConvertUsing<TeamMemberListToTeamConverter>();
  • CreateMap<TeamMember, Team>() converts Team to TeamMember
  • CreateMap<TeamMember, People>() converts People to TeamMember
  • CreateMap<IEnumerable<TeamMember>, IEnumerable<Team>>() converts a list of TeamMember to a list of Team using a custom converter
Custom Converter
C#
public class TeamMemberListToTeamConverter : ITypeConverter<IEnumerable<TeamMember>, IEnumerable<Team>>
{
    public IEnumerable<Team> Convert(IEnumerable<TeamMember> source, IEnumerable<Team> destination, ResolutionContext context)
    {
        /*
        List<int> ids = new List<int>();
        foreach (var item in source)
        {
            int id = item.TeamId;
            if (ids.Contains(id))
            {
                continue;
            }
            var model = context.Mapper.Map<Team>(item);
            model.Members = new List<People>();
            foreach (var people in source.Where(x => x.TeamId == model.Id && x.TeamName == model.Name))
            {
                model.Members.Add(context.Mapper.Map<People>(people));
            }
            ids.Add(id);
            yield return model;
        }
        */

        var teams = source.DistinctBy(m => new { m.TeamId, m.TeamName }).Select(member => context.Mapper.Map<Team>(member));
        foreach (var team in teams)
        {
            team.Members = new List<People>();
            foreach (var member in source.Where(m => m.TeamId == team.Id && m.TeamName == team.Name))
            {
                team.Members.Add(context.Mapper.Map<People>(member));
            }
            yield return team;
        }
    }
}

The converter is using previously configured TeamMember to Team and TeamMember to People mappings.

Using Mapper

C#
[HttpPost("[action]")]
public IEnumerable<Team> 
       ListToComplexObjectList([FromBody] IEnumerable<TeamMember> list)
{
    return Mapper.Map<IEnumerable<Team>>(list);
}

What's Next?

  • Conversion based on language preference

About Code Sample

  • Visual Studio 2022 Solution
  • ASP.NET 6, Web API project
  • This example is also tested in 5, 3.1

History

  • 27th July, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Bangladesh Bangladesh
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Mou_kol3-Aug-22 3:34
Mou_kol3-Aug-22 3:34 
GeneralRe: My vote of 5 Pin
DiponRoy3-Aug-22 4:16
DiponRoy3-Aug-22 4:16 
QuestionA couple of suggestions. Pin
George Swan28-Jul-22 20:19
mveGeorge Swan28-Jul-22 20:19 

Thanks for the interesting piece. As has already been stated, you explain AutoMapper much better than the official documentation. Your TeamMemberListToTeamConverter class works well but, as the Convert method is not referenced directly but called internally by AutoMapper, it cannot readily be debugged. That is unfortunate as it is the one of the principal methods in the application.

Looking at your class, it seems to me that it can be refactored by using the extension DistinctBy. Something like.

C#
public class TeamMemberListToTeamConverter :
  ITypeConverter<IEnumerable<TeamMember>, IEnumerable<Team>>
 {
     public IEnumerable<Team> Convert(IEnumerable<TeamMember> teamMembers,
               IEnumerable<Team> teams, ResolutionContext context)
     {
         teams = teamMembers.DistinctBy(m => m.TeamId)
                            .Select(member => context.Mapper.Map<Team>(member));
         foreach (var team in teams)
         {
             team.Members = new List<People>();
             foreach (var member in teamMembers.Where
                              (m => m.TeamId == team.Id && m.TeamName == team.Name))
             {
                 team.Members.Add(context.Mapper.Map<People>(member));
             }
             yield return team;
         }
     }
 }


On further analysis, it should be possible to replace the entire functionality of AutoMapper by a single function that implements the necessary mapping within it. Something along the lines of

C#
 public static IEnumerable<Team> ConvertTeamMembersToTeams(IEnumerable<TeamMember> teamMembers)
{
    var teams = teamMembers.DistinctBy(m => m.TeamId)
               .Select(member => new Team() { Id = member.TeamId, Name = member.TeamName, Members = new() });
    foreach (var team in teams)
    {
        foreach (var member in teamMembers.Where
                        (m => m.TeamId == team.Id && m.TeamName == team.Name))
        {
            team.Members.Add(new People() { Id = member.PeopleId, Name = member.PeopleName });
        }
        yield return team;
    }
}

Although the above instantiates several classes, it would be my preferred choice as it is simple to debug and removes the need to import the AutoMapper Package. Thanks again and best wishes, George


AnswerRe: A couple of suggestions. Pin
DiponRoy28-Jul-22 23:52
DiponRoy28-Jul-22 23:52 
QuestionWhat is a POGO model? Pin
Robert Bernstein28-Jul-22 14:07
professionalRobert Bernstein28-Jul-22 14:07 
AnswerRe: What is a POGO model? Pin
DiponRoy28-Jul-22 19:40
DiponRoy28-Jul-22 19:40 
QuestionThank you Pin
Marc Clifton27-Jul-22 14:09
mvaMarc Clifton27-Jul-22 14:09 
AnswerRe: Thank you Pin
DiponRoy27-Jul-22 19:44
DiponRoy27-Jul-22 19:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.