Как игнорировать нулевые значения для всех элементов-источников во время сопоставления в Automapper 6?

Я искал повсюду: stackoverflow, документацию automapper, internets и просто не мог найти никакой информации об этом, даже если это кажется очень распространенной проблемой.

Мое отображение:

CreateMap<StatusLevelDTO, StatusLevel>()
            .ForAllMembers(opt => opt.Condition(src => src != null));

Это не работает, потому что src представляет исходный объект (StatusLevelDTO), а не свойство source (я думаю).

Чтобы быть более конкретным, если я сопоставляю ObjectA с ObjectB, ObjectA.SomeValue имеет значение null, а ObjectB.SomeValue равно 2, я хочу, чтобы объект назначения сохранял свое значение (2).

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

Есть ли способ сделать это в Automapper 6? Я использую 6.0.2, чтобы быть точным.

Ответ 1

Метод Condition теперь имеет пять перегрузок, один из которых принимает предикат типа

Func<TSource, TDestination, TMember, bool>

этот параметр TMember является исходным элементом. Таким образом, вы можете проверить исходный элемент для null:

CreateMap<StatusLevelDTO, StatusLevel>()
     .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));

Ответ 2

Решение здесь работает для моего проекта, который использует AutoMapper 6.0.2. В предыдущих проектах, использующих AutoMapper 4, я использовал IsSourceValueNull для достижения того же поведения.

Я внес небольшое изменение в оригинальное решение. Вместо проверки типа сопоставляемого свойства я установил фильтр в ForAllPropertyMaps, чтобы проверить тип исходного объекта, чтобы настраиваемый распознаватель применялся только к картам из этого исходного объекта. Но фильтр может быть установлен на что угодно по мере необходимости.

var config = new MapperConfiguration(cfg =>
{
    cfg.ForAllPropertyMaps(
        pm => pm.TypeMap.SourceType == typeof(<class of source object>),
        (pm, c) => c.ResolveUsing<object, object, object, object>(new IgnoreNullResolver(), pm.SourceMember.Name));
});

class IgnoreNullResolver : IMemberValueResolver<object, object, object, object>
{
    public object Resolve(object source, object destination, object sourceMember, object destinationMember, ResolutionContext context)
    {
        return sourceMember ?? destinationMember;
    }
}

Ответ 3

Я вдохновился @Sergey Berezovskiy и сделал эту конфигурацию для всех членов всех карт в основной конфигурации:

Mapper.Initialize(cfg =>
{
  cfg.ForAllMaps((obj, cnfg) => cnfg.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null)));
}

Ответ 4

Поскольку у меня нет репутации, чтобы комментировать, я добавлю здесь свой ответ для @Sikor @sensei

Если вы используете модель, которая имеет типы данных DTO, допускающие значение NULL, вы можете использовать этот метод расширения ниже, чтобы свести к минимуму влияние Automapper на использование значения по умолчанию для определенного типа данных.

Модельные примеры

public class Foo {
    public bool? Example { get; set; }
}

public class FooDto {
    public bool Example { get; set; }
}

Метод продления:

public static TTarget MapModelProperties<TTarget, TSource>(this TTarget target, TSource source) where TTarget : class
                                                                                                where TSource : class

    {
        // Map target into the source, where the source property is null
        Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<TTarget, TSource>()
                .ForAllMembers(opt => opt.Condition((src, dest, srcMember, destMember) => destMember == null));
        });
        Mapper.Map(target, source);

        // Map the source into the target to apply the changes
        Mapper.Initialize(cfg => cfg.CreateMap<TSource, TTarget>());
        Mapper.Map(source, target);

        return target;
    }

использование

public class Foo
{
    public bool? Example { get; set; }
}

public class FooDto
{
    public bool Example { get; set; }
}

public void Example()
{
    var foo = new Foo
    {
        Example = null
    };

    var fooDto = new FooDto
    {
        Example = true
    };

    fooDto.MapModelProperties(foo);
}