Карта сложного типа для плоского типа в AutoMapper

У меня есть сложный объект вроде:

public class BusinessUnit
{
        public TradingDesk TradingDesk { get; }
        public string Division { get; }

        public BusinessUnit(string division, TradingDesk tradingDesk)
        {
            Division = division;
            TradingDesk = tradingDesk;
        }
}

Я хочу сопоставить это с плоским типом:

public class Row
{
   //TradingDesk properties
   public string TraderFirstName { get; set; }
   public string TraderLastName { get; set; }
   public string TradingDeskName { get; set; }

   public string Division { get; set; }
}

Я уже настроил AutoMapper для TradingDesk:

CreateMap<TradingDesk, Row>().ForMember(vm => vm.TradingDeskName, op => op.MapFrom(src => src.Name));

так что следующий тест проходит:

[Test]
public void Should_Map_TradingDesk_To_Row()
{
    var tradingDesk = Fixture.Create<TradingDesk>();

    var mapped = AutoMapper.Map<Row>(tradingDesk);

    mapped.TradingDeskName.Should()
            .Be(tradingDesk.Name);
    mapped.TraderFirstName.Should()
            .Be(tradingDesk.Trader.FirstName);
    mapped.TraderLastName.Should()
            .Be(tradingDesk.Trader.LastName);
}

Но когда я пытаюсь сопоставить BusinessUnit с Row, мне приходится переконфигурировать AutoMapper для TradingDesk как таковой:

CreateMap<BusinessUnit, Row>()
   .ForMember(vm => vm.TradingDeskName, op => op.MapFrom(src => src.TradingDesk.Name))
   .ForMember(vm => vm.TraderFirstName, op => op.MapFrom(src => src.TradingDesk.Trader.FirstName))
   .ForMember(vm => vm.TraderLastName, op => op.MapFrom(src => src.TradingDesk.Trader.LastName));

Я ожидаю, что AutoMapper должен использовать уже настроенное сопоставление источника и целевого типа, когда ему нужно сопоставить TradingDesk с Row при отображении BusinessUnit. Таким образом, я могу построить конфигурацию от наименьшего до самого большого типа, сглаживая сложный объект без необходимости определять отображение для каждого отдельного элемента в сплющенном типе.

Ответ 1

Фактический синтаксис может отличаться, потому что я использую AutoMapper статическим образом, но принцип остается тем же:

Mapper.CreateMap<BusinessUnit, Row>()
      .ConvertUsing(source => Mapper.Map<TradingDesk, Row>(source.TradingDesk));

Ответ 2

Сначала я думаю, что ваша карта для торгового стола будет просто

CreateMap<TradingDesk, Row>();

Если память мне подходит, так как ваш участник-получатель соответствует имени члена-источника, когда "." удаляются, тогда вам не нужно явно указывать его.

Если вы измените свой класс Row на этот

public class Row
{
   //TradingDesk properties
   public string TradingDeskTraderFirstName { get; set; }
   public string TradingDeskTraderLastName { get; set; }
   public string TradingDeskName { get; set; }

   public string Division { get; set; }
}

ваша карта будет

CreateMap<BusinessUnit, Row>(); 

Если вы никогда не собираетесь сопоставлять с TradingDesk, удалите эту карту.

Если это определено в каком-либо профиле, вы также можете сказать

RecognizePrefixes("TradingDesk"); 

и вам не нужно менять класс Row.

Ответ 3

Если вы не хотите создавать специальные преобразователи или преобразователи свойств, вы можете выполнить второе сопоставление после карты основного объекта, например:

Mapper.Initialize(cfg =>
{
    cfg.CreateMap<TradingDesk, Row>()
        .ForMember(vm => vm.TradingDeskName, op => op.MapFrom(src => src.Name));
    cfg.CreateMap<BusinessUnit, Row>()
        .AfterMap((businessUnit, row) => { Mapper.Map(businessUnit.TradingDesk, row); });
});

var source = new BusinessUnit("division", new TradingDesk { TraderFirstName = "FirstName", TraderLastName = "LastName", Name = "DeskName" });

var dest = Mapper.Map<Row>(source);

В этом случае объект был отображен без создания нового экземпляра.