Как игнорировать нулевые значения для всех элементов-источников во время сопоставления в 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);
}