Automapper Flatten Nested Collections

Automapper Flatten Nested Collections

I try to figure out how to flatten a collection of Merchants, each containing a collection of Orders to a flat List of OrderViewModels.

Here my DTO:

public class Merchant
{
    public string MerchantName { get; set; }
    public List<Order> Orders { get; set; }
}

public class Order
{
    public string OrderId { get; set; }
}

And Here's the view model:

public class OrderViewModel
{
    public string MerchantName { get; set; }
    public string OrderId { get; set; }
}

My Goal is to flatten a List<Merchant> to a List<OrderViewModel> whereas the following test structure should result in 6 view models:

var myMerchants = new List<Merchant>
{
    new Merchant
    {
        MerchantName = "Merchant X",
        Orders = new List<Order>
        {
             new Order { OrderId = "Order 1"},
             new Order { OrderId = "Order 2"},
             new Order { OrderId = "Order 3"}
        }
    },
    new Merchant
    {
        MerchantName = "Merchant Y",
        Orders = new List<Order>
        {
            new Order { OrderId = "Order 4"},
            new Order { OrderId = "Order 5"},
            new Order { OrderId = "Order 6"}
        }
    }
 };

 var models = Mapper.Map<List<OrderViewModel>>(myMerchants);

4 Answers

Because the cardinality of the root objects isn't 1:1, (i.e. 2 root Merchants need to map to 6 OrderViewModels), you may need to resort to a custom TypeConverter and operate at the collection level, where you can use .SelectMany to do the flattening:

public class MyTypeConverter : ITypeConverter<IEnumerable<Merchant>, List<OrderViewModel>>
{
    public List<OrderViewModel> Convert(ResolutionContext context)
    {
        if (context == null || context.IsSourceValueNull)
            return null;

        var source = context.SourceValue as IEnumerable<Merchant>;

        return source
            .SelectMany(s => s.Orders
              .Select(o => new OrderViewModel
              {
                  MerchantName = s.MerchantName,
                  OrderId = o.OrderId
              }))
              .ToList();
    }
}

Which you can then bootstrap:

Mapper.CreateMap<IEnumerable<Merchant>, List<OrderViewModel>>()
    .ConvertUsing<MyTypeConverter>();

And then mapped as such:

var models = Mapper.Map<List<OrderViewModel>>(myMerchants);
0

An interesting finding is that, only do the below is enough to achieve the goal without automapper.

var models = myMerchants.SelectMany(s => s.Orders.Select(o => new OrderViewModel { MerchantName = s.MerchantName, OrderId = o.OrderId })).ToList();

Old question but thought would be helpful for newer versions.

I am using .Net core 2 with automapper. I prefer to do the ProjectTo extension of queryable

queryable
   .SelectMany(outterClass => outterClass.innerList)
   .AsQueryable()
   .ProjectTo<OutterClassDto>();

Then, configure like so:

config.CreateMap<OuterClass, OuterClassDto>();

It's also possible to combine SelectMany with AutoMapper and do the mapping for each item in the flattened collection. You need two mappings for this Merchant -> OrderViewModel and Order -> OrderViewModel

var models = myMerchants.SelectMany(s => s.Orders.Select(o => 
{
    var mappedItem = Mapper.Map<OrderViewModel>(s);
    Mapper.Map(o, mappedItem);
    return mappedItem;
})).ToList();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Chloe Bennett
Author

Chloe Bennett

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.