Usando AutoMapper para desplegar una DTO

He estado tratando de usar AutoMapper para ahorrar algo de tiempo desde mis DTO a mis objetos de dominio, pero estoy teniendo problemas para configurar el mapa para que funcione, y estoy empezando a preguntarme si AutoMapper podría ser la herramienta incorrecta para el trabajo.

Considere este ejemplo de objetos de dominio (una entidad y un valor):

public class Person { public string Name { get; set; } public StreetAddress Address { get; set; } } public class StreetAddress { public string Address { get; set; } public string City { get; set; } public string State { get; set; } } 

Mi DTO (de un objeto Linq-to-SQL) está saliendo más o menos así:

 public class PersonDTO { public string Name { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } } 

Me gustaría poder hacer esto en mi repository:

 return Mapper.Map(result); 

Intenté configurar AutoMapper en todos los sentidos, pero sigo obteniendo la configuración genérica del mapa de tipos faltantes o un error de mapeo no compatible , sin detalles para decirme dónde estoy fallando.

He intentado varias configuraciones diferentes, pero aquí hay algunas:

 Mapper.CreateMap() .ForMember(dest => dest.Address, opt => opt.MapFrom(Mapper.Map)); 

y

 Mapper.CreateMap() .ForMember(dest => dest.Address.Address1, opt => opt.MapFrom(src => src.Address)) .ForMember(dest => dest.Address.City, opt => opt.MapFrom(src => src.City)) .ForMember(dest => dest.Address.State, opt => opt.MapFrom(src => src.State)); 

He leído que aplanar objetos con AutoMapper es fácil, pero desestabilizarlos no es fácil … ni siquiera posible. ¿Alguien puede decirme si estoy tratando de hacer lo imposible, y si no lo estoy haciendo mal?

Tenga en cuenta que mis objetos reales son un poco más complicados, por lo que es posible que omita la información que es la clave del error … si lo que estoy haciendo se ve bien puedo proporcionar más información o comenzar a simplificar mis objetos para probar .

use https://github.com/omuleanu/ValueInjecter , aplana y no ajusta, y cualquier otra cosa que necesite, hay una aplicación de muestra asp.net mvc en la descarga donde se muestran todas las características (también pruebas unitarias)

Esto también parece funcionar para mí:

 Mapper.CreateMap(); Mapper.CreateMap() .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src ))); 

Básicamente, cree un mapeo desde el dto a ambos objetos y luego úselo como fuente para el objeto hijo.

No se puede publicar un comentario, por lo que publicar una respuesta. Supongo que hubo algunos cambios en la implementación de AutoMapper, por lo que la respuesta https://stackoverflow.com/a/5154321/2164198 propuesta por HansoS ya no es comstackble. Aunque hay otro método que se puede usar en tales escenarios: ResolveUsing :

 Mapper.CreateMap() .ForMember(dest => dest.Address, opt => opt.ResolveUsing( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; })) 

Además de la respuesta sydneyos y de acuerdo con el comentario de Trevor de Koekkoek, el mapeo bidireccional es posible de esta manera

 public class Person { public string Name { get; set; } public Address Address { get; set; } } public class Address { public string Street { get; set; } public string City { get; set; } public string State { get; set; } } public class PersonViewModel { public string Name { get; set; } public string AddressStreet { get; set; } public string AddressCity { get; set; } public string AddressState { get; set; } } 

Asignaciones de Automapper

 Mapper.Initialize(cfg => cfg.RecognizePrefixes("Address")); Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap() .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src ))); 

Si implementa la clase NameOf , puede deshacerse del prefijo magic string

 Mapper.Initialize(cfg => cfg.RecognizePrefixes(Nameof.Property(x => x.Address))); 

EDITAR: En C # 6

 Mapper.Initialize(cfg => cfg.RecognizePrefixes(nameof(Person.Address))); 

Esto puede ser tarde, pero puedes resolverlo usando expresiones lambda para crear el objeto así:

 Mapper.CreateMap() .ForMember(dest => dest.Address, opt => opt.MapFrom( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; })) 

Estoy usando esto

 public static void Unflatten(this IMemberConfigurationExpression opt) { var prefix = opt.DestinationMember.Name; var memberProps = typeof(TMember).GetProperties(); var props = typeof(TSource).GetProperties().Where(p => p.Name.StartsWith(prefix)) .Select(sourceProp => new { SourceProp = sourceProp, MemberProp = memberProps.FirstOrDefault(memberProp => prefix + memberProp.Name == sourceProp.Name) }) .Where(x => x.MemberProp != null); var parameter = Expression.Parameter(typeof(TSource)); var bindings = props.Select(prop => Expression.Bind(prop.MemberProp, Expression.Property(parameter, prop.SourceProp))); var resolver = Expression.Lambda>( Expression.MemberInit(Expression.New(typeof(TMember)), bindings), parameter); opt.ResolveUsing(resolver.Compile()); } 

Configuración

 new MapperConfiguration(cfg => { cfg.CreateMap(); cfg.CreateMap().ForMember(x => x.HomeAddress, opt => opt.Unflatten()); }); 

Modelos

 public class Person { public string Name { get; set; } public Address HomeAddress { get; set; } } public class Address { public string Line1 { get; set; } public string Line2 { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } } 

Siguiendo las convenciones de aplanamiento de AutoMapper

 public class PersonDTO { public string Name { get; set; } public string HomeAddressLine1 { get; set; } public string HomeAddressLine2 { get; set; } public string HomeAddressCity { get; set; } public string HomeAddressState { get; set; } public string HomeAddressZipCode { get; set; } } 

Probablemente necesite muchas mejoras, pero funciona …

Tengo otra solución. La idea principal es que AutoMapper sepa cómo aplanar los objetos nesteds al nombrar correctamente las propiedades en el objeto aplanado: agregar el nombre de la propiedad del objeto nested como un prefijo. Para su caso, la dirección es prefijo:

 public class PersonDTO { public string Name { get; set; } public string AddressCity { get; set; } public string AddressState { get; set; } ... } 

Por lo tanto, la creación de un mapeo familiar de nested a aplanado y luego utilizando el método ReverseMap permite a AutomMapper comprender cómo desbloquear el objeto nested .

 CreateMap() .ReverseMap(); 

¡Eso es todo!