Search code examples
c#.netasp.net-core-webapiautomapper

AutoMapper is not showing results in ASP.NET Core Web API application


I am using AutoMapper to map between different entities in ASP.NET Core Web API. However, it's not showing results, but if I do the mapping manually, it works - I want to know why that is.

I have entities Order and OrderProduct as a middle table to join the many-to-many relationship between Order and Product.

I have created a DTO for (Order, OrderProduct, Product) and do the mapping profile for them.

public class Order
{
    public Order()
    {
        OrderDate  = DateTime.Now;
    }

    public int Id { get; set; }
    public DateTime OrderDate { get; set; }

    public AppUser AppUser { get; set; }

    public ICollection<OrderProduct> OrderProducts { get; set; } = new HashSet<OrderProduct>()
}

public class OrderProduct
{
    public int Id { get; set; }

    public int OrderId { get; set; }
    public Order Order { get; set; }

    public int ProductId { get; set; }
    public Product Product { get; set; }
}

public class Product
{
    public int Id { get; set; }

    [Required, MaxLength(150)]
    public string PName { get; set; }

    [Required, MaxLength(250)]
    public string PDescription { get; set; }

    public decimal Price { get; set; }
}

And this is the DTO:

public class OrderDTO   
{         
    public int OrderId { get; set; }        
    public DateTime OrderDate { get; set; }      
    public string ProductName { get; set; }        

    public List<OrderProductDTO> OrderProductDTO { get; set; } = new List<OrderProductDTO>();     
}   

public class OrderProductDTO     
{         
    public int OrderId { get; set; }          
    public ProductDTO Products { get; set; }      
    public int ProductId { get; set; }        
    public string ProductName { get; set; }    
    public decimal Price { get; set; }        
    public int Quantity { get; set; }     
}   

public class ProductDTO     
{         
    public int productID { get; set; }        
    public string productName { get; set; }         
    public decimal productPrice { get; set; }        
    public int productRating { get; set; }         
    public string productImageURL { get; set; }   
}  

// MAPPING Profile  
 CreateMap<Order, OrderDTO>()                 
      .ForMember(dest => dest.OrderId, opt => opt.MapFrom(src => src.Id))                 
      .ReverseMap();          
               
CreateMap<OrderProduct, OrderProductDTO>()                 
      .ForMember(dest => dest.ProductName, opt => opt.MapFrom(src => src.Product.PName))                  
      .ForMember(dest => dest.OrderId , opt => opt.MapFrom(src => src.OrderId))                  
      .ForMember(dest => dest.ProductId, opt => opt.MapFrom(src => src.Product.Id))                  
      .ReverseMap();    
           
CreateMap<Product, ProductDTO>()   
      .ForMember(dsc => dsc.productID, src => src.MapFrom(src => src.Id))
      .ForMember(dsc => dsc.productName, src => src.MapFrom(src => src.PName))  
      .ForMember(dsc => dsc.productPrice, src => src.MapFrom(src => src.Price))   
      .ForMember(dsc => dsc.productImageURL, src => src.MapFrom(src => src.ImageUrl)) 
      .ForMember(dsc => dsc.productRating, src => src.MapFrom(src => src.Rate))                
      .ReverseMap();

Order controller (I am using a repository pattern)

public async Task<IReadOnlyList<Order>> GetOrdersByUsernameAsync(string username)
{
    return await _context.Set<Order>()
                         // .Include(o => o.AppUser)
                         .Include(o => o.OrderProducts)
                         .ThenInclude(op => op.Product)
                         .Where(o => o.AppUser.Id == username)
                         .ToListAsync();
}

[HttpGet("{userID}")]
public async Task<IActionResult> GetOrdersByUserId(string userID)
{
    var orders = await unitOfWork.Orders.GetOrdersByUsernameAsync(userID);

    if (orders == null)
    {
        return BadRequest($"There is no order yet or not found..");
    }

    // var result = _mapper.Map<IEnumerable<OrderDTO>>(orders);

    // return Ok(orders);
    // return Ok(result);

    var orderDTOs = new List<OrderDTO>();

    foreach (var order in orders)
    {
        var orderDTO = new OrderDTO
            {
                OrderId = order.Id,
                OrderProductDTO = new List<OrderProductDTO>()
            };

        foreach (var orderProduct in order.OrderProducts)
        {
            var orderProductDTO = new OrderProductDTO
                {
                    ProductId = orderProduct.ProductId,
                    ProductName = orderProduct.Product.PName, // manual mapping to product name
                    Price = orderProduct.Product.Price,
                    Quantity = orderProduct.Product.Quantity,
                };
            orderDTO.OrderProductDTO.Add(orderProductDTO);
        }

        orderDTOs.Add(orderDTO);
    }

    return Ok(orderDTOs);
}

The returned order without mapping

[
  {
    "id": 1017,
    "orderDate": "2023-04-13T21:38:19.2123593",
    "status": 0,
    "appUser": null,
    "orderProducts": [
      {
        "id": 0,
        "orderId": 1017,
        "productId": 1,
        "product": {
          "id": 1,
          "pName": "Nike Men Running Shoes Revolution 5",
          "pDescription": "Shoes",
          "price": 2000,
          "quantity": 50
        }
      },
      {
        "id": 0,
        "orderId": 1017,
        "productId": 2,
        "product": {
          "id": 2,
          "pName": "Supernova",
          "pDescription": "Shoes",
          "price": 2200,
          "quantity": 50
          
        }
      }
    ]
  }
]

with manual mapping;

[
  {
    "orderId": 1017,
    "orderDate": "0001-01-01T00:00:00",
    "productName": null,
    "orderProductDTO": [
     {
        "orderId": 0,
        "products": null,
        "productId": 1,
        "productName": "Nike Men Running Shoes Revolution 5",
        "price": 2000,
        "quantity": 50
      },
      {
        "orderId": 0,
        "products": null,
        "productId": 2,
        "productName": "Supernova",
        "price": 2200,
        "quantity": 50
      }
    ]
  }
]

With AutoMapper automatic mapping:

[
  {
    "orderId": 1017,
    "orderDate": "2023-04-13T21:38:19.2123593",
    "productName": null,
    "orderProductDTO": []   // Why is not Mapping Right? 
  }
]

I am expecting mapped result even when using AutoMapper ...


Solution

  • I have modified your code for the better readability in the answer per se.

    As @Drewskis mentioned , you need to rename the field in OrderDTO.

    And another one in the OrderProductDTO.

    Order

    public class Order
    {
        public Order()
        {
            OrderDate  = DateTime.Now;
        }
    
        public int Id { get; set; }
        public DateTime OrderDate { get; set; }
        public AppUser AppUser { get; set; }
        public ICollection<OrderProduct> OrderProducts { get; set; } = new HashSet<OrderProduct>()
    }
    
    public class OrderDTO   
    {         
        public int OrderId { get; set; }        
        public DateTime OrderDate { get; set; }      
        public string ProductName { get; set; }        
        public List<OrderProductDTO> OrderProducts { get; set; } = new List<OrderProductDTO>(); // Renamed from OrderProductDTO
    }
    
    CreateMap<Order, OrderDTO>()                 
          .ForMember(dest => dest.OrderId, opt => opt.MapFrom(src => src.Id))                 
          .ReverseMap();
    

    Order Product

    public class OrderProduct
    {
        public int Id { get; set; }
    
        public int OrderId { get; set; }
        public Order Order { get; set; }
    
        public int ProductId { get; set; }
        public Product Product { get; set; }
    }
    
    public class OrderProductDTO     
    {         
        public int OrderId { get; set; }          
        public ProductDTO Product { get; set; } // Renamed from Products
        public int ProductId { get; set; }        
        public string ProductName { get; set; }    
        public decimal Price { get; set; }        
        public int Quantity { get; set; }     
    }
    
    CreateMap<OrderProduct, OrderProductDTO>()                 
          .ForMember(dest => dest.ProductName, opt => opt.MapFrom(src => src.Product.PName))                  
          .ForMember(dest => dest.OrderId , opt => opt.MapFrom(src => src.OrderId))                  
          .ForMember(dest => dest.ProductId, opt => opt.MapFrom(src => src.Product.Id))                  
          .ReverseMap(); 
    

    Product

    public class Product
    {
        public int Id { get; set; }
    
        [Required, MaxLength(150)]
        public string PName { get; set; }
    
        [Required, MaxLength(250)]
        public string PDescription { get; set; }
    
        public decimal Price { get; set; }
    }
    
    public class ProductDTO     
    {         
        public int productID { get; set; }        
        public string productName { get; set; }         
        public decimal productPrice { get; set; }        
        public int productRating { get; set; }         
        public string productImageURL { get; set; }   
    }  
    
    CreateMap<Product, ProductDTO>()   
          .ForMember(dsc => dsc.productID, src => src.MapFrom(src => src.Id))
          .ForMember(dsc => dsc.productName, src => src.MapFrom(src => src.PName))  
          .ForMember(dsc => dsc.productPrice, src => src.MapFrom(src => src.Price))   
          .ForMember(dsc => dsc.productImageURL, src => src.MapFrom(src => src.ImageUrl)) 
          .ForMember(dsc => dsc.productRating, src => src.MapFrom(src => src.Rate))                
          .ReverseMap();