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); 0An 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();