Home > Software design >  AutoMapper - Convert list of objects to a single object, with a nested list
AutoMapper - Convert list of objects to a single object, with a nested list

Time:09-16

I'm trying to map list of objects to single object, with a nested list.

I have the following class

public class EventLog {
        public string SystemId { get; set; }
        public string UserId { get; set; }
        public List<Event> Events { get; set; }
}

public class Event {
        public string EventId { get; set; }
        public string Message { get; set; }
}

public class EventDTO {
        public string SystemId { get; set; }
        public string UserId { get; set; }
        public string EventId { get; set; }
        public string Message { get; set; }
}

In my List< EventDTO> SystemId and UserId is same for every item in the list.

I want to do exactly reverse of Possible to use AutoMapper to map one object to list of objects?.

Thank you!

CodePudding user response:

Due to the fact, that your DTOs could contain different SystemIds and / or UserIds, you'll have to group by this criterion before creating your grouped objects. As always, if needed to wave by hand an instance out from a given instance from another type, the catch all method in AutoMapper is .ConvertUsing() and taking an overload that provides a ResolutionContext to allow calling Map() methods within the method itself.

After giving this theoretical introduction, let's come to some real code that solves your problem:

public static class Program
{
    public static void Main(string[] args)
    {
        var dtos = new[] {
            new EventDTO { SystemId = "1", UserId = "10", EventId = $"Event {Guid.NewGuid()}", Message = $"Message {Guid.NewGuid()}" },
            new EventDTO { SystemId = "1", UserId = "20", EventId = $"Event {Guid.NewGuid()}", Message = $"Message {Guid.NewGuid()}" },
            new EventDTO { SystemId = "1", UserId = "30", EventId = $"Event {Guid.NewGuid()}", Message = $"Message {Guid.NewGuid()}" },
            new EventDTO { SystemId = "2", UserId = "10", EventId = $"Event {Guid.NewGuid()}", Message = $"Message {Guid.NewGuid()}" },
            new EventDTO { SystemId = "2", UserId = "20", EventId = $"Event {Guid.NewGuid()}", Message = $"Message {Guid.NewGuid()}" },
            new EventDTO { SystemId = "2", UserId = "30", EventId = $"Event {Guid.NewGuid()}", Message = $"Message {Guid.NewGuid()}" },
        };

        var config = new MapperConfiguration(conf => conf.AddProfile<EventMappingProfile>());
        var mapper = config.CreateMapper();

        // Have to ask for list, cause DTOs could differ in SystemId and/or UserId
        var logs = mapper.Map<List<EventLog>>(dtos);

        // Write result to console
        foreach (var log in logs)
            foreach (var ev in log.Events)
                Console.WriteLine($"{log.SystemId} {log.UserId} {ev.EventId} {ev.Message}");

        Console.ReadKey();
    }

}

public class EventMappingProfile : Profile
{
    public EventMappingProfile()
    {
        CreateMap<EventDTO, Event>();
        CreateMap<IEnumerable<EventDTO>, IEnumerable<EventLog>>()
            .ConvertUsing((dtos, _, context) =>
            {
                return dtos
                    .GroupBy(dto => (dto.SystemId, dto.UserId))
                    .Select(group => new EventLog
                    {
                        SystemId = group.Key.SystemId,
                        UserId = group.Key.UserId,
                        Events = group.Select(context.Mapper.Map<Event>).ToList()
                    })
                    .ToList();
            });
    }
}

CodePudding user response:

You will need to setup the following mapping:

CreateMap<EventDTO, Event>();
CreateMap<EventDTO, EventLog>();
CreateMap<IEnumerable<EventDTO>, EventLog>()
    .ForMember(dest => dest.SystemId, opt => opt.MapFrom(src => src.FirstOrDefault() != null ? src.First().SystemId : ""))
    .ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.FirstOrDefault() != null ? src.First().UserId : ""))
    .ForMember(dest => dest.Events, opt => opt.MapFrom(src => src));

And call the following method:

var eventLog = Mapper.Map<IEnumerable<EventDTO>, EventLog>(events);
  • Related