resolutioncontext - AutoMapper: ¿Cuál es la diferencia entre MapFrom y ResolveUsing?
automapper resolutioncontext (5)
Acabo de hacer algunos puntos de referencia con el nuevo operador condicional nulo C # 6 ?.
Considere el siguiente escenario: la clase A
tiene un hijo clase B
, que tiene un hijo C
, cuya propiedad Name
queremos aplanar en un DTO. He probado dos 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));
Llamé a _mapper.Map<ResolveUsingXDto>(x);
o _mapper.Map<MapFromADto>(a);
para 1000 ResolveUsingX x
y MapFromA a
y se tomó el tiempo usando un System.Diagnostics.StopWatch
. Aquí están mis resultados:
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
tiene que capturar NullReferenceExceptions, que es más lento que ResolveUsing
con el operador de elvis ?.
Ignorar las sobrecargas de ResolveUsing
que toman un IValueResolver, y mirando solo estos 2 métodos:
void ResolveUsing(Func<TSource, object> resolver);
void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember);
La principal diferencia entre estos 2 parece ser que ResolveUsing
toma un Func<TSource, object>
, mientras que MapFrom toma una Expression<Func<TSource, TMember>>
.
Sin embargo, en el código de cliente que realmente usa uno de estos métodos con una expresión lambda, parecen ser intercambiables:
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));
Entonces, ¿cuál es la diferencia entre las 2 opciones anteriores? ¿Es uno más rápido que el otro? ¿Es una opción mejor que la otra y si es así, cuándo / por qué?
Aunque en muchas situaciones se puede usar cualquiera, según la documentación oficial, hay una diferencia cuando se trata de proyecciones LINQ. La explicación detallada se puede encontrar here .
Larga historia corta: usa MapFrom siempre que sea posible.
En el pasado tuve un largo intercambio de correos electrónicos en la lista de correo con el autor de Automapper. MapFrom realizará verificaciones nulas durante todo el proceso a través de la expresión:
Por lo tanto, puede hacer
opt => opt.MapFrom(src => src.SomeProp.Way.Down.Here.Somewhere)
y cada nivel se revisará para los nulos (como ya lo hace para el aplanamiento).
Según el código fuente, ResolveUsing
es más complicado. El valor de origen puede ser cualquier objeto; por lo tanto, puede usar cualquier valor que desee para llenar el miembro de destino, como int o bool que obtiene al "Resolver" el objeto dado. Sin embargo, MapFrom
solo usa miembros para mapear.
/// <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);
MapFrom
tiene algunas inteligencias adicionales . Por ejemplo (de la lista de correo ):
En MapFrom, trato de ser inteligente en cuanto a profundizar en las propiedades de los niños (como lo hace el aplanamiento normal). MapFrom es un intento de imitar el aplanamiento, con un poco adicional de permitir la redirección. ResolveUsing no tiene este comportamiento.
No estoy seguro de si esto está completamente documented cualquier lugar (aparte del código fuente ).