AssertConfigurationIsValid () при использовании вложенного ConstructUsing, не игнорируя частные установщики - PullRequest
0 голосов
/ 23 октября 2019

В следующем примере (LinqPad):

void Main()
{

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, DestinationNested>()
            .ConstructUsing((source, context) => new DestinationNested(source.InnerValue));

        cfg.CreateMap<Source, DestinationOuter>()
            .ForMember(x => x.OuterValue, y => y.MapFrom(z => z.OuterValue))
            .ConstructUsing((source, context) =>
            {
                return new DestinationOuter(source.OuterValue, context.Mapper.Map<DestinationNested>(source));
            });

    });

    var src = new Source { OuterValue = 999, InnerValue = 111 };

    var mapper = config.CreateMapper();
    var mapped = mapper.Map<DestinationOuter>(src);

    mapped.Dump();

    mapper.ConfigurationProvider.AssertConfigurationIsValid();
}

public class Source
{
    public int OuterValue { get; set; }
    public int InnerValue { get; set; }
}

public class DestinationOuter
{
    public int OuterValue { get; private set; }
    public DestinationNested destinationNested { get; private set; }

    public DestinationOuter(int outerValue, DestinationNested destinationNested)
    {
        this.OuterValue = outerValue;
        this.destinationNested = destinationNested;
    }
}

public class DestinationNested
{
    public int NestedValue { get; private set; }

    public DestinationNested(int nestedValue)
    {
        this.NestedValue = nestedValue;
    }
}

В настоящее время AssertConfigurationIsValid () вызывает исключение, касающееся свойств, так как я использую ContructUsing.

На практике это правильно отображает,но я бы хотел, чтобы AssertConfigurationIsValid был частью моего набора тестов для поиска регрессий (без необходимости выполнять ручные тесты маппера).

Я хотел бы подтвердить, что все мои свойства отображаются из источника впункт назначения через конструктор. Я хочу использовать конструктор, так как это мой уровень Домена, и этот конструктор применяет обязательные элементы.

Я не хочу игнорировать все частные сеттеры с помощью функции IgnoreAllPropertiesWithAnInaccessibleSetter (), поскольку я могу игнорировать что-то, что яна самом деле не установлено.

В идеале мне также не нужно выполнять ручную Ignore () для каждого из свойств, которые появляются в конструкторе, поскольку это оставляет пробел для смещения кода.

Я пробовал разные комбинации в Automapper, но пока не повезло.

Полагаю, это проблема статического анализа;Я хотел бы знать, что мой конструктор охватывает все свойства в пункте назначения. И я хотел бы знать, что в конструктор передается все из источника.

Я понимаю, что Automapper пока не работает очень автоматически, есть ли хороший способ использовать autopper для этого тестирования илиЭто вместо статического анализа проблема?

Ответы [ 2 ]

1 голос
/ 26 октября 2019

Вот мой дубль.

static void Main(string[] args)
{
    try{
    var mapperCfg = new AutoMapper.MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, DestinationOuter>().ForCtorParam("destinationNested", o => o.MapFrom(s => new DestinationNested(s.InnerValue)));
    });
    mapperCfg.AssertConfigurationIsValid();
    var mapper = mapperCfg.CreateMapper();

    var src = new Source { OuterValue = 999, InnerValue = 111 };
    mapper.Map<DestinationOuter>(src).Dump();
    }catch(Exception ex){
        ex.ToString().Dump();
    }
}
public class Source
{
    public int OuterValue { get; set; }
    public int InnerValue { get; set; }
}
public class DestinationOuter
{
    public int OuterValue { get; }
    public DestinationNested DestinationNested { get; }

    public DestinationOuter(int outerValue, DestinationNested destinationNested)
    {
        this.OuterValue = outerValue;
        this.DestinationNested = destinationNested;
    }
}
public class DestinationNested
{
    public int NestedValue { get; private set; }

    public DestinationNested(int nestedValue)
    {
        this.NestedValue = nestedValue;
    }
}
0 голосов
/ 25 октября 2019

После прочтения большого количества документов, прохождения интеграционных тестов с отладчиком и нескольких дней хороших экспериментов, это лучшее, что у меня есть:

var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, DestinationNested>()
            .ForCtorParam("nestedValue", x => x.MapFrom(y => y.InnerValue))
            .ForMember(x => x.NestedValue, x => x.MapFrom(y => y.InnerValue));

        cfg.CreateMap<Source, DestinationOuter>()
            .ForPath(x => x.destinationNested.NestedValue, x => x.MapFrom(y => y.InnerValue))
            .ForCtorParam("destinationNested", x => x.MapFrom(y => y));
    });

Я очень доволен этим;он избавляется от запаха ContructUsing (), который в моей более широкой кодовой базе создавал вложенные объекты. И это предупреждает меня, если мой целевой объект не заполнен. В идеале, строка параметров конструктора должна быть безопасной для типов, но я понимаю, почему она не может (возможно, это что-то для веселого проекта анализатора кода Roslyn на другой день :-))

Секретный соус (горячий пресс)) был x => x.MapFrom(y => y) в сочетании с .ForPath(x => x.destinationNested.NestedValue, x => x.MapFrom(y => y.InnerValue)), который, по-видимому, давал AutoMapper достаточно подсказок о том, что destinationNested был связан с InnerValue и что параметр contructor был переименован в "destinationNested". Магия заключалась в том, что вместо того, чтобы использовать контекст для создания вложенного объекта, невинно выглядящий x.MapFrom(y => y), казалось, позволил ему вместо этого использовать отображение свойств *.

* Это объяснение моего непрофессионала, я еще не следовалдостаточно исходного кода AutoMapper, чтобы правильно понять связь между отображением свойств и отображением конструктора. Читая некоторые билеты на GitHub, я подумал, что они были отдельными понятиями.

Я также не видел x.MapFrom(y => y), упомянутых в документах, поэтому мне было бы интересно узнать больше об этом.

...