Automapper - 将上层 属性 作为参数传递给下层地图

Automapper - Passing an upper level property as a parameter to lower level map

我希望能够在注释映射部分访问 ShipmentIdentifier 属性,这样我就可以 运行 下面的断言语句成功。下面是我的映射器的简化版本。我如何在映射 WeightUnit 时传递 ShipmentIdentifier 属性?

using AutoMapper;
using System.Collections.Generic;
using Xunit;

namespace ConsoleApp1
{
class Program
{
    static void Main(string[] args)
    {
        IMapper mapper = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(typeof(PrintLabelCommandMappingProfile));
        }).CreateMapper();

        CarrierLabelPrintedMessage message = new CarrierLabelPrintedMessage
        {
            ShipmentIdentifier = "AB1234",
            Cartons = new List<Carton>
            {
                new Carton
                {
                    TrackingNumber = "TestTrackingNumber",
                    LabelData =
                        new LabelData
                        {
                            Weight = 5,
                            WeightUnit = "none"
                        }
                }
            }
        };

        var command = mapper.Map<PrintLabelCommand>(message);
        Assert.Equal(command.ShipmentUnits[0].Weight.Unit, WeightUnit.None);
    }

    public class PrintLabelCommandMappingProfile : Profile
    {
        public PrintLabelCommandMappingProfile()
        {
            CreateMap<CarrierLabelPrintedMessage, PrintLabelCommand>()
                .ForMember(cmd => cmd.ShipmentId, exp => exp.MapFrom(msg => msg.ShipmentIdentifier))
                .ForMember(cmd => cmd.ShipmentUnits, exp => exp.MapFrom(msg => msg.Cartons));

            CreateMap<Carton, ShipmentUnit>()
                .ForMember(su => su.Id, exp => exp.MapFrom(c => c.TrackingNumber))
                .ForPath(su => su.Weight.Amount, exp => exp.MapFrom(c => c.LabelData.Weight))
                .ForPath(su => su.Weight.Unit, exp => exp.MapFrom(c => c.LabelData.WeightUnit));

            CreateMap<string, WeightUnit>().ConvertUsing((str, _) =>
            {
                // I want to have ShipmentIdentifier field to be accessible in the upper level to implement the mapping logic here as below
                //if (ShipmentIdentifier.StartsWith("AB")) // it's "AB1234" in the sample
                //  return WeightUnit.None;
                //else
                    return WeightUnit.Grams;
            });
        }
    }

    public class CarrierLabelPrintedMessage
    {
        public string ShipmentIdentifier { get; set; }
        public List<Carton> Cartons { get; set; }
    }

    public class PrintLabelCommand
    {
        public string ShipmentId { get; set; }
        public List<ShipmentUnit> ShipmentUnits { get; set; }
    }

    public class Carton
    {
        public string TrackingNumber { get; set; }
        public LabelData LabelData { get; set; }
    }

    public class LabelData
    {
        public double Weight { get; set; }
        public string WeightUnit { get; set; }
    }

    public class ShipmentUnit
    {
        public string Id { get; set; }
        public Weight Weight { get; set; }
    }

    public class Weight
    {
        public double Amount { get; set; }
        public WeightUnit Unit { get; set; }
    }

    public enum WeightUnit
    { 
        None,
        Grams,
        Kilograms
    }
}
}

有什么想法吗?

您可以在 CreateMap<CarrierLabelPrintedMessage, PrintLabelCommand>() 映射上使用 AfterMap(),以根据 CarrierLabelPrintedMessage 中可用的信息“修复”映射。映射可能如下所示:

CreateMap<CarrierLabelPrintedMessage, PrintLabelCommand>()
    .ForMember(cmd => cmd.ShipmentId, exp => exp.MapFrom(msg => msg.ShipmentIdentifier))
    .ForMember(cmd => cmd.ShipmentUnits, exp => exp.MapFrom(msg => msg.Cartons))
    .AfterMap((src, target) => {
        WeightUnit targetValue = WeightUnit.Grams;
        if (src.ShipmentIdentifier.StartsWith("AB"))
        {
            targetValue = WeightUnit.None;
        }
        foreach (ShipmentUnit shipmentUnit in target.ShipmentUnits)
        {
            shipmentUnit.Weight.Unit = targetValue;
        }
    });