Search code examples
c#automapperautomapper-6

Mapping objects with a field that is another class


I am mapping an object to an object model as shown below:

        CreateMap<Order, OrderModel>()
            .ForMember(result =>
                result.OrderId,
                opt => opt.MapFrom(source => source.OrderId))
            .ForMember(result =>
                result.Quantity,
                opt => opt.MapFrom(source => source.Quantity))
            .ReverseMap();

Here are the object and model:

        public class Order
        {
            public int OrderId { get; set; }
            public int Quantity { get; set; }
            public int ItemId { get; set; }

        }

        public class OrderModel
        {
            public int OrderId { get; set; }
            public int Quantity { get; set; }
            public Item ItemPurchased { get; set; }

        }

And here is the class of the ItemPurchased field:

        public class Item
        {
            public int ItemId { get; set; }
            public string ItemName { get; set; }          
        }

My question is how do I handle the Item? The Item itself is another class. How do I map it so that the Item object is obtained using the itemId, and then mapped to the OrderModel? What is the typical way to do this? Or is this not a practical or typical way to have a class member that is another class?

I googled for a typical solution, but was unsuccessful.


Solution

  • To be honest I personally don't like having these kind of things happening implicitly (it's kind of hard to change later), but it's not the end of the world.

    Automapper has something called Custom Value Resolvers (https://docs.automapper.org/en/stable/Custom-value-resolvers.html#custom-value-resolvers) which allows you to define some logic when mapping an object.

    You should be able to do something like this:

    public class CustomResolver : IValueResolver<Order, OrderModel, Item>
    {
        private readonly ItemService _itemService;
        // I'm assuming you're using dependency injection here.
        public CustomResolver(ItemService itemService) {
          this._itemService = itemService;
        }
    
        public int Resolve(Order source, OrderModel destination, Item member, ResolutionContext context)
        {
            return this._itemService.GetItemById(source.ItemId);
        }
    }
    

    And then your mapping profile would look something like this:

    CreateMap<Order, OrderModel>()
            .ForMember(result =>
                result.OrderId,
                opt => opt.MapFrom(source => source.OrderId))
            .ForMember(result =>
                result.Quantity,
                opt => opt.MapFrom(source => source.Quantity))
            .ForMember(result => result.ItemPurchased ,
                opt => opt.MapFrom<CustomResolver>());
    

    The only downside to this is you can't use "ReverseMapping", but it's not that bad.

    Hope this helps!