web-dev-qa-db-ja.com

AutoMapper:MapFromとResolveUsingの違いは何ですか?

IValueResolverを受け取るResolveUsingオーバーロードを無視し、次の2つのメソッドのみを確​​認します。

void ResolveUsing(Func<TSource, object> resolver);
void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember);

これら2つの主な違いは、ResolveUsingFunc<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));

それでは、上記の2つの選択肢の違いは最終的に何でしょうか?一方が他方より速いですか?一方が他方よりも良い選択であり、もしそうなら、いつ/なぜですか?

50
danludwig

過去には、Automapperの作成者と メーリングリストでの長いメール交換 がありました。 MapFromは、式全体でnullチェックを実行します。

したがって、opt => opt.MapFrom(src => src.SomeProp.Way.Down.Here.Somewhere)を実行すると、各レベルでnullのチェックが行われます(既にフラット化の場合と同様)。

64
Sunny Milenov

新しいC#6を使用していくつかのベンチマークを行いました null条件演算子 _?._

次のシナリオを考えてみましょう。クラスAには子クラスBがあり、これには子Cがあり、そのNameプロパティはDTOにフラット化します。 2つのバリアントをテストしました。

_// 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));
_

1000個の_ResolveUsingX x_および_MapFromA a_に対して_mapper.Map<ResolveUsingXDto>(x);または_mapper.Map<MapFromADto>(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をキャッチする必要があり、elvis演算子_?._を使用したResolveUsingよりも遅い

19

MapFromには いくつかの特別なスマート があります。たとえば( メーリングリスト から):

MapFromでは、(通常のフラット化と同様に)子プロパティを掘り下げることを賢くしようとします。 MapFromは、リダイレクトを許可するビットを追加して、フラット化を模倣する試みです。 ResolveUsingにはこの動作はありません。

これが完全に 文書化された どこか( ソースコード 以外)かどうかはわかりません。

9
Mightymuke

公式ドキュメント に基づいて、多くの状況でどちらかを使用できますが、LINQプロジェクションに関しては違いがあります。詳細な説明は こちら にあります。

簡単に言えば、可能な限りMapFromを使用します。

1

ソースコードによると、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);
0
Eliko