validate objects formember custom complex mapping automapper

mapping - objects - automapper projection



Usando AutoMapper para desplegar una DTO (7)

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 repositorio:

return Mapper.Map<PersonDTO, Person>(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<PersonDTO, Person>() .ForMember(dest => dest.Address, opt => opt.MapFrom(Mapper.Map<Person, Domain.StreetAddress>));

y

Mapper.CreateMap<Person, Domain.Person>() .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 .


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<Person, PersonViewModel>(); Mapper.CreateMap<PersonViewModel, Address>(); Mapper.CreateMap<PersonViewModel, Person>() .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<Person>.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<Person, Domain.Person>() .ForMember(dest => dest.Address, opt => opt.MapFrom( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; }))


Esto también parece funcionar para mí:

Mapper.CreateMap<PersonDto, Address>(); Mapper.CreateMap<PersonDto, Person>() .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.


Estoy usando esto

public static void Unflatten<TSource, TDestination, TMember>(this IMemberConfigurationExpression<TSource, TDestination, TMember> 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<Func<TSource, TMember>>( Expression.MemberInit(Expression.New(typeof(TMember)), bindings), parameter); opt.ResolveUsing(resolver.Compile()); }

Configuración

new MapperConfiguration(cfg => { cfg.CreateMap<Person, PersonDTO>(); cfg.CreateMap<PersonDTO, Person>().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 ...


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://.com/a/5154321/2164198 propuesta por HansoS ya no es compilable. Aunque hay otro método que se puede usar en tales escenarios: ResolveUsing :

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


Tengo otra solución. La idea principal es que AutoMapper know cómo aplanar los objetos anidados al nombrar correctamente las propiedades en el objeto aplanado: agregar el nombre de la propiedad del objeto anidado 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 anidado a aplanado y luego utilizando el método ReverseMap permite a AutomMapper comprender cómo desbloquear el objeto anidado .

CreateMap<Person, PersonDTO>() .ReverseMap();

¡Eso es todo!


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)