Home > Enterprise >  Automapper - Mapping is not working for record type
Automapper - Mapping is not working for record type

Time:07-06

I am trying to map a class to a record and it seems to blow up everytime.

The class I have is

public class CreateStockRequestDto 
{
    public CreateStockRequestDto(string name, string locationId, int quantity)
    {
        Name = name;
        LocationId = locationId;
        Quantity = quantity;
    }

    public string Name { get; init; }

    public int Quantity { get; set; }

    public string? Sku { get; set; }

    public string LocationId { get; init; }
}

I want it to map to a record

public record Stock
{
    public Stock(StockId stockId, string itemName, int quantity, string? sku = null)
    {
        if (!Guid.TryParse(stockId.Id, out _))
        {
            throw new ArgumentException($"Invalid stock id {stockId.Id}", nameof(stockId));
        }

        if (quantity <= 0)
        {
            throw new ArgumentException($"Invalid quantity : {quantity}", nameof(quantity));
        }

        if (!IsValidItemName(itemName))
        {
            throw new ArgumentException($"Invalid item name {itemName}", nameof(itemName));
        }

        StockId = stockId;
        ItemName = itemName;
        Quantity = quantity;
        Sku = sku;
    }

    public StockId StockId { get; init; }
    public string ItemName { get; init; }
    public int Quantity { get; init; }
    public string? Sku { get; init; }

    private static bool IsValidItemName(string itemName)
    {
        return Regex.IsMatch(itemName, RegularExpressions.ItemNameRegex);
    }
}

I am using Automapper for mapping functionality, but I keep getting an error which states

Location.Domain.ValueObjects.Stock needs to have a constructor with 0 args or only optional args. Validate your configuration for details. (Parameter 'type')

public class StockProfile : Profile
{
    public StockProfile()
    {
        CreateMap<Stock, CreateStockResponseDto>()
            .ForMember(dest => dest.Id, input => input.MapFrom(src => src.StockId.Id))
            .ForMember(dest => dest.Name, input => input.MapFrom(src => src.ItemName));

        CreateMap<CreateStockRequestDto, Stock>()
            .ForPath(x => x.StockId, opt => opt.MapFrom(o => new StockId(Guid.NewGuid().ToString())))
            .ForMember(dest => dest.Quantity, opt => opt.MapFrom(o => o.Quantity))
            .ForMember(dest => dest.ItemName, act => act.MapFrom(src => src.Name));
    }
}

CodePudding user response:

For your scenario, you need Constructor mapping by providing the constructor parameters via .ForCtorParam().

public class StockProfile : Profile
{
    public StockProfile()
    {
        ...

        CreateMap<CreateStockRequestDto, Stock>()
            .ForCtorParam("quantity", opt => opt.MapFrom(src => src.Quantity))
            .ForCtorParam("stockId", opt => opt.MapFrom(src => new StockId(Guid.NewGuid().ToString())))
            .ForCtorParam("itemName", opt => opt.MapFrom(src => src.Name));
    }
}

Sample NET Fiddle

  • Related