AutoMapper: What is the difference between MapFrom and ResolveUsing?

后端 未结 5 1880
执笔经年
执笔经年 2020-11-30 07:00

Ignoring the ResolveUsing overloads that take an IValueResolver, and looking only at these 2 methods:

v         


        
相关标签:
5条回答
  • 2020-11-30 07:36

    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.

    0 讨论(0)
  • 2020-11-30 07:42

    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).

    0 讨论(0)
  • 2020-11-30 07:50

    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).

    0 讨论(0)
  • 2020-11-30 07:51

    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 ?.

    0 讨论(0)
  • 2020-11-30 07:58

    According to the source code, ResolveUsing is more complicated. The source value can be any object; therefore, you can use any value you want to fill the destination member, such as int or bool that you get by "Resolving" the given object. However, MapFrom only uses member to map from.

    /// <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 讨论(0)
提交回复
热议问题