In this part we learn about Nested Mapping in Automapper and we will use the same OrderDto Object that we had previously but we will let Automapper to map the inner objects.
Once again here is the Domain Objects First
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//Order public class Order { public string OrderNo { get; set; } public Customer Customer { get; set; } public DateTime PurchaseDate { get; set; } public IEnumerable<OrderItems> LineItems { get; set; } public bool ShipToHomeAddress { get; set; } public decimal GetTotal() { return LineItems == null ? 0 : LineItems.Sum(x => x.GetTotalPrice()); } public Guid InternalId { get; set; } } //OrderItems public class OrderItems { public decimal Price { get; set; } public string Name { get; set; } public int Quantity { get; set; } public decimal GetTotalPrice() { return Price * Quantity; } } |
The Dto (Data Transfer Object) look like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class OrderDto { public string CustomerName { get; set; } public decimal Total { get; set; } public string OrderNumber { get; set; } public IEnumerable<OrderItemsDto> LineItems { get; set; } } //OrderItemsDto public class OrderItemsDto { public string Name { get; set; } public int Quantity { get; set; } public decimal Price { get; set; } } |
This time when we map Order to OrderDto we will also provide the mapping of OrderItems to OrderItemsDto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public ActionResult OrderItems() { var orders = _respository.GetAll(); Mapper.CreateMap<Order, OrderDto>() .ForMember(dest => dest.OrderNumber, opt => opt.MapFrom(src => src.OrderNo)); Mapper.CreateMap<OrderItems, OrderItemsDto>(); var model = Mapper.Map<IEnumerable<Order>, IEnumerable<OrderDto>>(orders); return View(model); } //View <table> <tr> <th>Order Number</th> <th>Name</th> <th>Total</th> <th>Items</th> </tr> @foreach (var item in Model) { <tr> <td>@item.OrderNumber</td> <td>@item.CustomerName</td> <td>$@item.Total</td> <td> @foreach (var child in item.LineItems) { @string.Format("({0}) {1} - ${2}", @child.Quantity, @child.Name, @child.Price)<br /> } </td> </tr> } </table> |
In the next part I will be showing projection in Automapper.
Previous Post:
Part 1 NullSubsitution
Part 2 Flattening by Convention
Have you used an sql profiler to see the queries sent to the database? The code might look clean but might hide some unoptimized sql queries, that will not be good on production.
Valid point but in my demo I am not using sql, my repository is just a bunch of objects that are manually created. I do use sqlserver in another project and have not found a big performance issue since we have a dba who is optimizing all the time, and in my last part of this series I will be showing some tips on performance with Automapper.
Mapper.CreateMap()
.ForMember(dest => dest.OrderNumber, opt => opt.MapFrom(src => src.OrderNo));
How I can map property from nullable nested object?
In situation when orderItem.Order == null a had Object reference not set to an instance of an object exception.
One can use the NullSubsitute in my first posting I showed how to use a NullSubsitute of it. Or you could consider Ignore so that it doesnt map.
http://taswar.zeytinsoft.com/2011/03/07/automapper-mapping-objects-part-1-of-7-nullsubsitution/
[…] Automapper: Mapping objects Part 3 of 7 (Nested Mapping) […]
Do you still use Automapper or there is a better alternative to this? I am having challenges mapping multiple sources into a single destination.
I dont use Automapper anymore since I dont do much .NET development these days.
But here are some alternatives.
EmitMapper, http://emitmapper.codeplex.com/
ValueInjecter https://github.com/omuleanu/ValueInjecter
BLToolkit https://github.com/igor-tkachev/bltoolkit
This doesn’t seem to work if there is only 1 parent and many children. For example…If you were only bringing back a single Order with multiple Order Items.
This
var model = Mapper.Map<IEnumerable, IEnumerable>(orders);
would turn into this
var model = Mapper.Map(orders);
I think without the IEnumerable on the parent, it is not loading the child list.
My code samples got messed up when I hit Post. I hope you get the gist of what I am saying. I will try it again.
var model = Mapper.Map<IEnumerable, IEnumerable>(orders);
var model = Mapper.Map(order);
Just wondering can you not go with Mapper.Map < Order, IEnum > .
I found the solution for this in https://dotnetsolutionsbyvenkat.blogspot.com/2020/07/how-to-map-properties-of-one-class-to.html.
Nice artical with good explanation