.net - mvc - automapper net core
AutoMapper: "Ignorar el resto"? (16)
¿Cómo preferiría especificar que ciertos miembros sean ignorados? ¿Existe una convención, clase base o atributo que le gustaría aplicar? Una vez que se dedica a la tarea de especificar todas las asignaciones explícitamente, no estoy seguro de qué valor obtendría de AutoMapper.
¿Hay alguna manera de decirle a AutoMapper que ignore todas las propiedades, excepto las que están mapeadas explícitamente?
Tengo clases externas de DTO que es probable que cambien desde el exterior y deseo evitar especificar que cada propiedad se ignore explícitamente, ya que agregar nuevas propiedades romperá la funcionalidad (causar excepciones) cuando intente mapearlas en mis propios objetos.
A partir de AutoMapper 5.0, la propiedad .TypeMap
en IMappingExpression
se ha ido, lo que significa que la solución 4.2 ya no funciona. Creé una solución que usa la funcionalidad original pero con una sintaxis diferente:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Src, Dest>();
cfg.IgnoreUnmapped(); // Ignores unmapped properties on all maps
cfg.IgnoreUnmapped<Src, Dest>(); // Ignores unmapped properties on specific map
});
// or add inside a profile
public class MyProfile : Profile
{
this.IgnoreUnmapped();
CreateMap<MyType1, MyType2>();
}
Implementación:
public static class MapperExtensions
{
private static void IgnoreUnmappedProperties(TypeMap map, IMappingExpression expr)
{
foreach (string propName in map.GetUnmappedPropertyNames())
{
if (map.SourceType.GetProperty(propName) != null)
{
expr.ForSourceMember(propName, opt => opt.Ignore());
}
if (map.DestinationType.GetProperty(propName) != null)
{
expr.ForMember(propName, opt => opt.Ignore());
}
}
}
public static void IgnoreUnmapped(this IProfileExpression profile)
{
profile.ForAllMaps(IgnoreUnmappedProperties);
}
public static void IgnoreUnmapped(this IProfileExpression profile, Func<TypeMap, bool> filter)
{
profile.ForAllMaps((map, expr) =>
{
if (filter(map))
{
IgnoreUnmappedProperties(map, expr);
}
});
}
public static void IgnoreUnmapped(this IProfileExpression profile, Type src, Type dest)
{
profile.IgnoreUnmapped((TypeMap map) => map.SourceType == src && map.DestinationType == dest);
}
public static void IgnoreUnmapped<TSrc, TDest>(this IProfileExpression profile)
{
profile.IgnoreUnmapped(typeof(TSrc), typeof(TDest));
}
}
Actualicé la extensión de Can Gencer para no sobrescribir ningún mapa existente.
public static IMappingExpression<TSource, TDestination>
IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof (TSource);
var destinationType = typeof (TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType) && x.DestinationType.Equals(destinationType));
foreach (var property in existingMaps.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
Uso:
Mapper.CreateMap<SourceType, DestinationType>()
.ForMember(prop => x.Property, opt => opt.MapFrom(src => src.OtherProperty))
.IgnoreAllNonExisting();
En la versión de 3.3.1, simplemente puede usar los IgnoreAllPropertiesWithAnInaccessibleSetter()
o IgnoreAllSourcePropertiesWithAnInaccessibleSetter()
.
Este es un método de extensión que escribí que ignora todas las propiedades no existentes en el destino. No estoy seguro si todavía será útil ya que la pregunta tiene más de dos años, pero me encontré con el mismo problema al tener que agregar muchas llamadas Ignorar manualmente.
public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>
(this IMappingExpression<TSource, TDestination> expression)
{
var flags = BindingFlags.Public | BindingFlags.Instance;
var sourceType = typeof (TSource);
var destinationProperties = typeof (TDestination).GetProperties(flags);
foreach (var property in destinationProperties)
{
if (sourceType.GetProperty(property.Name, flags) == null)
{
expression.ForMember(property.Name, opt => opt.Ignore());
}
}
return expression;
}
Uso:
Mapper.CreateMap<SourceType, DestinationType>()
.IgnoreAllNonExisting();
ACTUALIZACIÓN : Aparentemente, esto no funciona correctamente si tiene asignaciones personalizadas porque las sobrescribe. Supongo que aún podría funcionar si llama primero a IgnoreAllNonExisting y luego a las asignaciones personalizadas más tarde.
schdr tiene una solución (como respuesta a esta pregunta) que utiliza Mapper.GetAllTypeMaps()
para descubrir qué propiedades no están mapeadas y las ignora automáticamente. Parece una solución más robusta para mí.
Han pasado algunos años desde que se formuló la pregunta, pero este método de extensión me parece más limpio, utilizando la versión actual de AutoMapper (3.2.1):
public static IMappingExpression<TSource, TDestination> IgnoreUnmappedProperties<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
if (typeMap != null)
{
foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
{
expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
}
}
return expression;
}
He actualizado la respuesta de Robert Schroeder para AutoMapper 4.2. Con configuraciones de mapper no estáticas, no podemos usar Mapper.GetAllTypeMaps()
, pero la expression
tiene una referencia al TypeMap
requerido:
public static IMappingExpression<TSource, TDestination>
IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
foreach (var property in expression.TypeMap.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
La única información sobre ignorar a muchos miembros es este hilo: http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f . Creo que puedes usar el truco utilizado en ProvidingCommonBaseClassConfiguration para ignorar propiedades comunes para clases similares.
Y no hay información sobre la funcionalidad "Ignorar el resto". Miré el código antes y me parece que será muy difícil agregar dicha funcionalidad. También puede intentar usar algún atributo y marcar con él las propiedades ignoradas y agregar algún código genérico / común para ignorar todas las propiedades marcadas.
La versión 5.0.0-beta-1 de AutoMapper presenta el método de extensión ForAllOtherMembers
para que pueda hacer lo siguiente:
CreateMap<Source, Destination>()
.ForMember(d => d.Text, o => o.MapFrom(s => s.Name))
.ForMember(d => d.Value, o => o.MapFrom(s => s.Id))
.ForAllOtherMembers(opts => opts.Ignore());
Tenga en cuenta que existe una ventaja al mapear explícitamente cada propiedad, ya que nunca tendrá problemas de fallas en el mapeo que surgen cuando se olvida de mapear una propiedad.
Tal vez en su caso sea sabio ignorar a todos los demás miembros y agregar un TODO
para volver y hacer que estos sean explícitos una vez que se establezca la frecuencia de los cambios en esta clase.
Para Automapper 5.0 con el fin de omitir todas las propiedades no asignadas solo necesita poner
.ForAllOtherMembers (x => x.Ignore ());
al final de tu perfil
Por ejemplo:
internal class AccountInfoEntityToAccountDtoProfile : Profile
{
public AccountInfoEntityToAccountDtoProfile()
{
CreateMap<AccountInfoEntity, AccountDto>()
.ForMember(d => d.Id, e => e.MapFrom(s => s.BankAcctInfo.BankAcctFrom.AcctId))
.ForAllOtherMembers(x=>x.Ignore());
}
}
En este caso, solo se resolverá el campo Id para el objeto de salida, todos los demás serán omitidos. Funciona como un encanto, parece que ya no necesitamos extensiones complicadas.
Para aquellos que están usando la API no estática en la versión 4.2.0 y superior, se puede utilizar el siguiente método de extensión (que se encuentra here en la clase AutoMapperExtensions
):
// from http://.com/questions/954480/automapper-ignore-the-rest/6474397#6474397
public static IMappingExpression IgnoreAllNonExisting(this IMappingExpression expression)
{
foreach(var property in expression.TypeMap.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
Lo importante aquí es que una vez que se elimina la API estática, el código como Mapper.FindTypeMapFor
ya no funcionará, de ahí el uso del campo expression.TypeMap
.
Parece una vieja pregunta, pero pensé que publicaría mi respuesta para cualquiera que se pareciera a mí.
Yo uso ConstructUsing, el inicializador de objetos junto con ForAllMembers ignoran, por ejemplo
Mapper.CreateMap<Source, Target>()
.ConstructUsing(
f =>
new Target
{
PropVal1 = f.PropVal1,
PropObj2 = Map<PropObj2Class>(f.PropObj2),
PropVal4 = f.PropVal4
})
.ForAllMembers(a => a.Ignore());
Por lo que entendí, la pregunta era que hay campos en el destino que no tienen un campo mapeado en la fuente, razón por la cual está buscando formas de ignorar esos campos de destino no mapeados.
En lugar de implementar y usar estos métodos de extensión, simplemente podría usar
Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Source);
Ahora el automapper sabe que solo necesita validar que todos los campos de origen estén mapeados, pero no al revés.
Pude hacer esto de la siguiente manera:
Mapper.CreateMap<SourceType, DestinationType>().ForAllMembers(opt => opt.Ignore());
Mapper.CreateMap<SourceType, DestinationType>().ForMember(/*Do explicit mapping 1 here*/);
Mapper.CreateMap<SourceType, DestinationType>().ForMember(/*Do explicit mapping 2 here*/);
...
Nota: Estoy usando AutoMapper v.2.0.
Puede usar ForAllMembers, que sobreescribir solo necesario como este
public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
{
expression.ForAllMembers(opt => opt.Ignore());
return expression;
}
Tenga cuidado, ignorará todo, y si no va a agregar una asignación personalizada, ya están ignorados y no funcionarán
también, quiero decir, si tienes una unidad de prueba para AutoMapper. Y prueba que todos los modelos con todas las propiedades mapeadas correctamente no deberían usar dicho método de extensión
deberías escribir ignorar explícitamente
Sé que esta es una vieja pregunta, pero @jmoerdyk en su pregunta:
¿Cómo usarías esto en una expresión de CreateMap () encadenada en un perfil?
puede usar esta answer esta manera dentro del Perfil ctor
this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));