55

忽略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>>

然而,在实际使用这些方法之一和 lambda 表达式的客户端代码中,它们似乎是可以互换的:

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

那么上述两种选择之间的最终区别是什么?一个比另一个快吗?一个是比另一个更好的选择吗?如果是,什么时候/为什么?

4

5 回答 5

70

过去,我在邮件列表中与 Automapper 的作者进行了长时间的电子邮件交流。MapFrom 将通过表达式一直执行 null 检查:

所以你可以这样做opt => opt.MapFrom(src => src.SomeProp.Way.Down.Here.Somewhere),每个级别都会检查是否有空值(就像它已经为展平所做的那样)。

于 2013-02-15T00:08:11.797 回答
22

我刚刚使用新的 C# 6 null 条件运算符做了一些基准测试 ?.

考虑以下场景:类A有一个子类B,它有一个子类,我们希望将CName属性展平为 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);or _mapper.Map<MapFromADto>(a);1000 个不同的电话ResolveUsingX xMapFromA 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 运算符要慢?.

于 2016-03-13T16:20:49.827 回答
9

MapFrom一些额外的智慧。例如(来自邮件列表):

在 MapFrom 中,我尽量聪明地挖掘子属性(就像正常的展平一样)。MapFrom 是一种模仿扁平化的尝试,增加了一点允许重定向。ResolveUsing 没有这种行为。

我不确定这是否在任何地方都有完整的记录(除了源代码)。

于 2013-02-14T20:14:01.187 回答
1

尽管在许多情况下都可以使用任何一种,但根据官方文档,在 LINQ 投影方面存在差异。详细解释可以在这里找到。

长话短说:尽可能使用 MapFrom。

于 2016-04-07T11:09:12.317 回答
0

根据源代码,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);
于 2014-02-19T09:18:12.323 回答