tutorial net mvc example ejemplo custom c# nested automapper flatten

c# - net - ¿Una mejor manera de usar AutoMapper para aplanar objetos anidados?



automapper nuget (5)

He estado aplanando objetos de dominio en DTO como se muestra en el siguiente ejemplo:

public class Root { public string AParentProperty { get; set; } public Nested TheNestedClass { get; set; } } public class Nested { public string ANestedProperty { get; set; } } public class Flattened { public string AParentProperty { get; set; } public string ANestedProperty { get; set; } } // I put the equivalent of the following in a profile, configured at application start // as suggested by others: Mapper.CreateMap<Root, Flattened>() .ForMember ( dest => dest.ANestedProperty , opt => opt.MapFrom(src => src.TheNestedClass.ANestedProperty) ); // This is in my controller: Flattened myFlattened = Mapper.Map<Root, Flattened>(myRoot);

He visto varios ejemplos, y hasta ahora esta parece ser la manera de aplanar una jerarquía anidada. Sin embargo, si el objeto hijo tiene varias propiedades, este enfoque no guarda mucha codificación.

Encontré este ejemplo:

http://consultingblogs.emc.com/owainwragg/archive/2010/12/22/automapper-mapping-from-multiple-objects.aspx

pero requiere instancias de los objetos asignados, requeridos por la función Mapa (), que no funcionarán con un perfil como lo entiendo.

Soy nuevo en AutoMapper, por lo que me gustaría saber si hay una mejor manera de hacerlo.


2 soluciones más posibles:

Mapper.CreateMap<Nested, Flattened>() .ForMember(s=>s.AParentProperty, o=>o.Ignore()); Mapper.CreateMap<Root, Flattened>() .ForMember(d => d.ANestedProperty, o => o.MapFrom(s => s.TheNestedClass));

Un enfoque alternativo sería el siguiente, pero no pasaría el Mapper.AssertConfigurationIsValid() .

Mapper.CreateMap<Nested, Flattened>() //.ForMember map your properties here Mapper.CreateMap<Root, Flattened>() //.ForMember... map you properties here .AfterMap((s, d) => Mapper.Map(s.TheNestedClass, d));


En la última versión de AutoMapper, hay una convención de nomenclatura que puede usar para evitar múltiples declaraciones .ForMember.

En su ejemplo, si actualiza su clase aplanada para que sea:

public class Flattened { public string AParentProperty { get; set; } public string TheNestedClassANestedProperty { get; set; } }

Puedes evitar el uso de la declaración ForMember:

Mapper.CreateMap<Root, Flattened>();

Automapper (por convención) Root.TheNestedClass.ANestedProperty a Root.TheNestedClass.ANestedProperty en este caso. ¡Parece menos feo cuando estás usando nombres de clase reales, honestamente!


Escribí el método de extensión para resolver un problema similar:

public static IMappingExpression<TSource, TDestination> FlattenNested<TSource, TNestedSource, TDestination>( this IMappingExpression<TSource, TDestination> expression, Expression<Func<TSource, TNestedSource>> nestedSelector, IMappingExpression<TNestedSource, TDestination> nestedMappingExpression) { var dstProperties = typeof(TDestination).GetProperties().Select(p => p.Name); var flattenedMappings = nestedMappingExpression.TypeMap.GetPropertyMaps() .Where(pm => pm.IsMapped() && !pm.IsIgnored()) .ToDictionary(pm => pm.DestinationProperty.Name, pm => Expression.Lambda( Expression.MakeMemberAccess(nestedSelector.Body, pm.SourceMember), nestedSelector.Parameters[0])); foreach (var property in dstProperties) { if (!flattenedMappings.ContainsKey(property)) continue; expression.ForMember(property, opt => opt.MapFrom((dynamic)flattenedMappings[property])); } return expression; }

Así que en tu caso puede ser usado así:

var nestedMap = Mapper.CreateMap<Nested, Flattened>() .IgnoreAllNonExisting(); Mapper.CreateMap<Root, Flattened>() .FlattenNested(s => s.TheNestedClass, nestedMap);

IgnoreAllNonExisting() es de here .

Aunque no es una solución universal, debería ser suficiente para casos simples.

Asi que,

  1. No es necesario seguir la convención de aplanamiento en las propiedades de destino.
  2. Mapper.AssertConfigurationIsValid () pasará
  3. También puede usar este método en API no estática (un perfil)

No estoy seguro de si esto agrega valor a las soluciones anteriores, pero podría hacerlo como una asignación de dos pasos. Tenga cuidado de asignar en el orden correcto si hay conflictos de nombres entre el padre y el hijo (las últimas victorias).

Mapper.CreateMap<Root, Flattened>(); Mapper.CreateMap<Nested, Flattened>(); var flattened = new Flattened(); Mapper.Map(root, flattened); Mapper.Map(root.TheNestedClass, flattened);


Para mejorar otra respuesta, especifique MemberList.Source para ambas asignaciones y establezca que la propiedad anidada se ignore. La validación luego pasa OK.

Mapper.Initialize(cfg => { cfg.CreateMap<SrcNested, DestFlat>(MemberList.Source); cfg.CreateMap<SrcRoot, DestFlat>(MemberList.Source) .ForSourceMember(s => s.Nested, x => x.Ignore()) .AfterMap((s, d) => Mapper.Map(s.Nested, d)); }); Mapper.AssertConfigurationIsValid(); var dest = Mapper.Map<SrcRoot, DestFlat>(src);