AutoMapper: В чем разница между MapFrom и ResolveUsing?
Игнорирование перегрузок ResolveUsing
, которые принимают IValueResolver, и глядя только на эти 2 метода:
void ResolveUsing(Func<TSource, object> resolver);
void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember);
Основное различие между этими двумя кажется, что ResolveUsing
принимает значение Func<TSource, object>
, тогда как MapFrom принимает Expression<Func<TSource, TMember>>
.
Однако в клиентском коде, который фактически использует один из этих методов с лямбда-выражением, они кажутся взаимозаменяемыми:
Mapper.CreateMap<SourceType, DestType>() // uses ResolveUsing
.ForMember(d => d.DestPropX, o => o.ResolveUsing(s => s.SourcePropY));
Mapper.CreateMap<SourceType, DestType>() // uses MapFrom
.ForMember(d => d.DestPropX, o => o.MapFrom(s => s.SourcePropY));
Итак, в чем же разница между двумя вышеуказанными вариантами? Быстрее, чем другой? Является ли лучший выбор, чем другой, и если да, то когда/почему?
Ответы
Ответ 1
В прошлом у меня был длинный обмен сообщениями в списке рассылки с автором Automapper. MapFrom будет выполнять нулевые проверки полностью через выражение:
Итак, вы можете сделать opt => opt.MapFrom(src =>
src.SomeProp.Way.Down.Here.Somewhere)
, и каждый уровень будет проверен для нулей (как это уже делается для выравнивания).
Ответ 2
Я только что сделал некоторые тесты, используя новый оператор С# 6 null условный ?.
Рассмотрим следующий сценарий: class A
имеет дочерний класс B
, который имеет дочерний элемент C
, свойство Name
, которое мы хотим сгладить в DTO. Я протестировал два варианта:
// using mapfrom
CreateMap<MapFromA, MapFromADto>()
.ForMember(dto => dto.Name, o => o.MapFrom(a => a.B.C.Name));
// using resolveusing with elvis
CreateMap<ResolveUsingX, ResolveUsingXDto>()
.ForMember(dto => dto.Name, o => o.ResolveUsing(x => x.Y?.Z?.Name));
Я назвал _mapper.Map<ResolveUsingXDto>(x);
или _mapper.Map<MapFromADto>(a);
для 1000 различных ResolveUsingX x
и MapFromA a
и занял время, используя System.Diagnostics.StopWatch
. Вот мои результаты:
Distinct elements per batch: 1000; # batches for average: 25
A->B->C.Name, C is never null.
MapForm - average time taken for 1000x: 5527,84 ticks = 1,44 ms.
ResolveUsing - average time taken for 1000x: 5479,76 ticks = 1,4 ms.
A->B->C.Name, C is null 1/3 of the time.
MapForm - average time taken for 1000x: 72924,4 ticks = 27,44 ms.
ResolveUsing - average time taken for 1000x: 5351,2 ticks = 1,48 ms.
A->B->C.Name, C is null 1/2 of the time.
MapForm - average time taken for 1000x: 107016,92 ticks = 40,52 ms.
ResolveUsing - average time taken for 1000x: 5835,32 ticks = 1,56 ms.
A->B->C.Name, C is null 2/3 of the time.
MapForm - average time taken for 1000x: 141437,96 ticks = 53,64 ms.
ResolveUsing - average time taken for 1000x: 5789,72 ticks = 1,56 ms.
MapFrom
должен поймать NullReferenceExceptions, который медленнее, чем ResolveUsing
с оператором elvis ?.
Ответ 3
MapFrom
имеет несколько дополнительных умений. Например (из список рассылки):
В MapFrom я стараюсь быть умным в том, чтобы вникать в дочерние свойства (как это делает нормальное сглаживание). MapFrom - это попытка подражать сглаживанию с добавлением бит разрешения перенаправления. ResolveUsing не имеет такого поведения.
Я не уверен, что это полностью документировано в любом месте (кроме исходный код).
Ответ 4
В соответствии с исходным кодом ResolveUsing
сложнее. Исходным значением может быть любой объект; поэтому вы можете использовать любое значение, которое вы хотите заполнить целевым элементом, например int или bool, которое вы получаете посредством "Разрешения" данного объекта. Однако MapFrom
использует только член для сопоставления.
/// <summary>
/// Resolve destination member using a custom value resolver callback. Used instead of MapFrom when not simply redirecting a source member
/// This method cannot be used in conjunction with LINQ query projection
/// </summary>
/// <param name="resolver">Callback function to resolve against source type</param>
void ResolveUsing(Func<TSource, object> resolver);
/// <summary>
/// Specify the source member to map from. Can only reference a member on the <typeparamref name="TSource"/> type
/// This method can be used in mapping to LINQ query projections, while ResolveUsing cannot.
/// Any null reference exceptions in this expression will be ignored (similar to flattening behavior)
/// </summary>
/// <typeparam name="TMember">Member type of the source member to use</typeparam>
/// <param name="sourceMember">Expression referencing the source member to map against</param>
void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember);
Ответ 5
Хотя во многих ситуациях либо можно использовать, основываясь на официальной документации существует разница, когда дело касается прогнозов LINQ. Подробное объяснение можно найти здесь.
Короче говоря: используйте MapFrom, когда это возможно.