Automapper: отображение одного объекта в список объектов с помощью конвертера и производных отображений - PullRequest
0 голосов
/ 22 сентября 2019

У меня есть требование отобразить один объект в список объектов.Я делаю это с помощью автоматического преобразователя типов.

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

Ниже приведен пример сокращенного кода.Я упростил условие проекции списка для краткости.

Иерархии наследования:

public class BaseA {
    public Guid Id1 { get; set; } = Guid.NewGuid();
}

public class BaseB {
    public Guid Id2 { get; set; } = Guid.NewGuid();
}

public class DerivedA : BaseA {
}

public class DerivedB : BaseB {
}

Преобразователь типов:

public class BaseAToBaseBConverter<TBaseA, TBaseB> : ITypeConverter<TBaseA, List<TBaseB>>
    where TBaseA : BaseA
    where TBaseB : BaseB
{
    public List<TBaseB> Convert(TBaseA source, List<TBaseB> destination, ResolutionContext context) {
        if (destination == null) {
            destination = new List<TBaseB>();
        }

        for (var i = 0; i < (int) context.Items["InstancesToCreate"]; i++) {
            destination.Add(context.Mapper.Map<TBaseA, TBaseB>(source));
        }

        return destination;
    }
}

Профиль отображения:

public class MappingProfile : Profile {
    public MappingProfile() {
        CreateMap<BaseA, BaseB>()
            .ForMember(destination => destination.Id2, o => o.MapFrom(source => source.Id1))
            .IncludeAllDerived();
        CreateMap<BaseA, List<BaseB>>().ConvertUsing<BaseAToBaseBConverter<BaseA, BaseB>>();

        CreateMap<DerivedA, DerivedB>();
        CreateMap<DerivedA, List<DerivedB>>().ConvertUsing<BaseAToBaseBConverter<DerivedA, DerivedB>>();
    }
}

Тесты:

private IMapper _mapper;

[SetUp]
public void SetUp() {
    _mapper = new MapperConfiguration(mc => {
        mc.AddProfile<MappingProfile>();
    }).CreateMapper();
}

[Test]
public void Map_SingleBaseAToList_ReturnsList() {
    var baseA = new BaseA();
    var baseB = _mapper.Map<BaseA, List<BaseB>>(baseA, options => options.Items["InstancesToCreate"] = 2);

    Assert.Multiple(() =>
    {
        Assert.That(baseB.Count, Is.EqualTo(2));
        Assert.That(baseB.Select(x => x.Id2).Distinct().Single(), Is.EqualTo(baseA.Id1));
    });
}

[Test]
public void Map_SingleDerivedBaseAToList_ReturnsList() {
    var derivedA = new DerivedA();
    var derivedB = _mapper.Map<DerivedA, List<DerivedB>>(derivedA, options => options.Items["InstancesToCreate"] = 2);

    Assert.Multiple(() => {
        Assert.That(derivedB.Count, Is.EqualTo(2));
        Assert.That(derivedB.Select(x => x.Id2).Distinct().Single(), Is.EqualTo(derivedA.Id1));
    });
}

Вышеприведенное работает отлично.Однако в действительности BaseA и BaseB являются абстрактными классами с кучей производных объектов, свисающих с них.Это означает, что я получаю достаточно длинный и очень повторяющийся профиль отображения.Я хотел бы знать, есть ли какой-то способ указать один ConvertUsing для единственного списка в базовых типах, и чтобы производные типы использовали это определение, как мы можем для отображения типа на тип.Что-то вроде следующего:

CreateMap<BaseA, BaseB>()
    .ForMember(destination => destination.Id2, o => o.MapFrom(source => source.Id1))
    .IncludeAllDerived();
CreateMap<BaseA, List<BaseB>>()         
    .ConvertUsing<BaseAToBaseBConverter<BaseA, BaseB>>()
    .IncludeAllDerived();

CreateMap<DerivedA, DerivedB>();
CreateMap<DerivedA, List<DerivedB>>();

Это недопустимо, поскольку нет прямого преобразования между List<BaseB> и List<DerivedB>.Есть ли другой способ заставить отображения работать так?

...