AutoMapper: What is the difference between MapFrom and ResolveUsing?
Ignoring the ResolveUsing
overloads that take an IValueResolver, and looking only at these 2 methods:
void ResolveUsing(Func<TSource, object> resolver);
void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember);
The main difference between these 2 seems to be that ResolveUsing
takes a Func<TSource, object>
, whereas MapFrom takes an Expression<Func<TSource, TMember>>
.
However in client code that actually uses one of these methods with a lambda expression, they seem to be interchangeable:
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));
So what ultimately is the difference between the above 2 choices? Is one faster than the other? Is one a better choice than the other and if so, when / why?
Solution 1:
In the past I had a long email exchange on the mailing list with the author of Automapper. MapFrom will do null checks all the way trough the expression:
So you can do
opt => opt.MapFrom(src => src.SomeProp.Way.Down.Here.Somewhere)
and each level will get checked for nulls (as it already does for flattening).
Solution 2:
I just did some benchmarks using the new C# 6 null conditional operator ?.
Consider the following scenario: class A
has a child class B
, which has a child C
, whose Name
property we want to flatten into a DTO. I tested two variants:
// 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));
I called _mapper.Map<ResolveUsingXDto>(x);
or _mapper.Map<MapFromADto>(a);
for 1000 different ResolveUsingX x
and MapFromA a
and took the time using a System.Diagnostics.StopWatch
. Here are my results:
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
has to catch NullReferenceExceptions, which is slower than ResolveUsing
with the elvis operator ?.
Solution 3:
MapFrom
has a few extra smarts. For example (from the mailing list):
In MapFrom, I try to be smart about digging in to child properties (much like the normal flattening does). MapFrom is an attempt to mimic flattening, with an added bit of allowing redirection. ResolveUsing doesn't have this behavior.
I'm not sure if this is fully documented anywhere (apart from in the source code).
Solution 4:
Although in many situations either can be used, based on official documentation there is a difference when it comes to LINQ projections. Detailed explanation can be found here.
Long story short: use MapFrom whenever possible.