Ignorer les surcharges ResolveUsing
qui prennent un IValueResolver et ne regarder que ces 2 méthodes:
void ResolveUsing(Func<TSource, object> resolver);
void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember);
La principale différence entre ces 2 semble être que ResolveUsing
prend un Func<TSource, object>
, alors que MapFrom prend un Expression<Func<TSource, TMember>>
.
Cependant, dans le code client qui utilise réellement l'une de ces méthodes avec une expression lambda, elles semblent interchangeables:
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));
Alors, quelle est la différence entre les 2 choix ci-dessus? Est-ce que l'un est plus rapide que l'autre? Est-ce que l'un est un meilleur choix que l'autre et si oui, quand/pourquoi?
Dans le passé, j’avais eu un échange de mails avec long sur la liste de diffusion avec l’auteur de Automapper. MapFrom effectuera des contrôles nuls tout au long de l'expression:
Donc, vous pouvez faire
opt => opt.MapFrom(src => src.SomeProp.Way.Down.Here.Somewhere)
et chaque niveau sera vérifié pour les valeurs nulles (comme il le fait déjà pour l'aplatissement).
Je viens de faire quelques tests en utilisant le nouvel opérateur conditionnel C # 6 null?.
Considérez le scénario suivant: class A
a un enfant class B
, qui a un enfant C
, dont nous voulons mettre à plat la propriété Name
dans un DTO. J'ai testé deux variantes:
// 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));
J'ai appelé _mapper.Map<ResolveUsingXDto>(x);
ou _mapper.Map<MapFromADto>(a);
pour 1000 codes ResolveUsingX x
et MapFromA a
différents et j'ai pris le temps en utilisant un System.Diagnostics.StopWatch
. Voici mes résultats:
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
doit capturer NullReferenceExceptions, qui est plus lente que ResolveUsing
avec l'opérateur elvis ?.
MapFrom
a quelques compétences supplémentaires . Par exemple (à partir de la liste de diffusion ):
Dans MapFrom, j’essaie d’être intelligent dans l’exploration de propriétés enfants (un peu comme l’aplatissement normal). MapFrom est une tentative d'imitation de l'aplatissement, avec un ajout supplémentaire permettant la redirection . ResolveUsing n'a pas ce comportement.
Je ne suis pas sûr que ce soit entièrement documenté où que ce soit (sauf dans le code source ).
Bien que dans de nombreuses situations, l’un ou l’autre soit utilisable, sur la base de documentation officielle , il existe une différence entre les projections LINQ. Des explications détaillées peuvent être trouvées ici .
Bref récit: utilisez autant que possible MapFrom.
Selon le code source, ResolveUsing
est plus compliqué. La valeur source peut être n'importe quel objet. par conséquent, vous pouvez utiliser n'importe quelle valeur que vous souhaitez renseigner dans le membre de destination, telle que int ou bool, obtenue en "Résolvant" l'objet donné. Cependant, MapFrom
utilise uniquement le membre à partir duquel mapper.
/// <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);