We have an API concerning invoices with invoicePositions. We are currently updating said API to move the currency from individual positions to the parent invoice
APIv1
public record InvoiceRequestV1 {
public IList<InvoicePositionRequestV1> InvoicePositions {get; init;}
// [...]
}
public record InvoicePositionRequestV1 {
public Currency Currency {get; init}
// [...]
}
APIv2
public record InvoiceRequestV2 {
public Currency Currency {get; init}
public IList<InvoicePositionRequestV2> InvoicePositions {get; init;}
// [...]
}
public record InvoicePositionRequestV1 {
// [...]
}
Dtos
public record InvoiceDto {
public Currency Currency {get; init}
public IList<InvoicePositionDtoV1> InvoicePositions {get; init;}
// [...]
}
public record InvoicePositionDto {
public Currency Currency {get; init}
// [...]
}
AutoMapper Profiles
public class InvoiceDtoProfileV1 : Profile {
public InvoiceDtoProfile() {
CreateMap<InvoiceRequestModelV1, InvoiceDto>()
.ForMember(d => d.Currency, m => m.Ignore());
}
}
public class InvoicePositionDtoProfileV1 : Profile {
public InvoiceDtoProfile() {
CreateMap<InvoiceRequestModelV1, InvoiceDto>();
}
}
We use AutoMapper to map from the incomming requestModel to our internal dtos. Currently both the InvoiceDto and the InvoicePositionDto have a property for Currency. What we would like to achieve is to use AutoMapper to map InvoiceRequestModelV2 including all positions to an InvoiceDto where all InvoicePositionDtos use the Currency from InvoiceRequestModel. Our current solution uses AutoMapper profiles for the base, but we have to manually update the Currency:
AutoMapper Profiles
public class InvoiceDtoProfileV2 : Profile {
public InvoiceDtoProfile() {
CreateMap<InvoiceRequestModelV2, InvoiceDto>();
}
}
public class InvoicePositionDtoProfileV2 : Profile {
public InvoiceDtoProfile() {
CreateMap<InvoiceRequestModelV2, InvoiceDto>()
.ForMember(d => d.Currency, m => m.Ignore());
}
}
Controller
invoiceRequestDto = invoiceRequestDto.UpdateCurrencyOnSalesInvoicePositions(salesInvoiceRequestModel.Currency);
ExtensionMethod
public static class InvoiceDtoExtensions {
public static InvoiceDto UpdateCurrencyOnSalesInvoicePositions(this InvoiceDto invoiceDto, Currency currency) {
var updatedSalesInvoicePositionDtos = invoiceDto.SalesInvoicePositions
.Select(invoicePositionDto => invoicePositionDto with { Currency = currency }).ToList();
return invoiceDto with { InvoicePositions = updatedSalesInvoicePositionDtos };
}
}
We have tried using an IMappingAction as AfterAction as follows:
public class UpdateCurrencyAction : IMappingAction<InvoiceRequestModelV2, InvoiceDto> {
public void Process(InvoiceRequestModelV2 source, InvoiceDto destination, ResolutionContext context) =>
invoiceDto.InvoicePositions = invoiceDto.InvoicePositions.Select(position => position.Currency = currency).ToList();
}
but that fails due to InvoiceDto and InvoicePositionDto being records (immutable). When using the property initialiser invoicePositionDto = invoicePositionDto with { Currency = invoiceDto.Currency };, we see, that the result is not used by AutoMapper.
Is there any way to implement what we want using AutoMapper functionality and C# records?
Answers I have looked into, but found unhelpful include:
- AutoMapper convert from multiple sources
- automapper multi objects to one object
- Transformation using AutoMapper
- Automapper: Mapping hierarchy
- Auto mapper Custom resolver source members to destination object list mapping issue
Most of these answers were unhelpful, because our InvoicePositionDto does not have a reference to its parent InvoiceDto (dito with RequestModel).