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

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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

You Might Also Like