AutoMapper: "Ignorar el resto"?


¿Hay alguna manera de decirle a AutoMapper que ignore todas las propiedades excepto las que están mapeadas explícitamente?

Tengo clases DTO externas que probablemente cambien desde el exterior y quiero 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.

Author: Igor Brejc, 2009-06-05

16 answers

Este es un método de extensión que escribí que ignora todas las propiedades no existentes en el destino. No estoy seguro de si seguirá siendo ú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 de ignorar manuales.

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();

UPDATE : Aparentemente esto no funciona correctamente si tiene asignaciones personalizadas porque las sobrescribe. Supongo que todavía podría funcionar si llama IgnoreAllNonExisting primero y luego la costumbre mapeos más tarde.

Schdr tiene una solución (como respuesta a esta pregunta) que usa Mapper.GetAllTypeMaps() para averiguar qué propiedades no están mapeadas e ignorarlas automáticamente. Me parece una solución más robusta.

 67
Author: Can Gencer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-14 07:01:33

He actualizado 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();
 207
Author: Robert Schroeder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-10 04:33:26

Por lo que entendí, la pregunta era que hay campos en el destino que no tienen un campo mapeado en el origen, por lo que 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.

 127
Author: Nazim Hafeez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-02 11:13:28

He podido 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.

 77
Author: Yohanb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-08 15:50:40

A partir de AutoMapper 5.0, la propiedad .TypeMap en IMappingExpression ha desaparecido, lo que significa que la solución 4.2 ya no funciona. He creado una solución que utiliza 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>();
}

Aplicació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));
    }
}
 38
Author: Richard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-27 11:50:18

La versión 5.0.0-beta-1 de AutoMapper introduce el método de extensión ForAllOtherMembers por lo que ahora puede hacer esto:

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 hay una ventaja de mapear explícitamente cada propiedad, ya que nunca tendrá problemas de mapeo fallando silenciosamente que surgen cuando se olvida de mapear una propiedad.

Tal vez en su caso podría ser prudente ignorar a todos los demás miembros y agregar un TODO para volver y hacerlos explícitos después de que se establezca la frecuencia de los cambios en esta clase.

 35
Author: ajbeaven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-03 05:19:06

Ha habido algunos años desde que se hizo la pregunta, pero este método de extensión me parece más limpio, usando 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;
}
 17
Author: Iravanchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-05 15:08:20

Para aquellos que están usando la API no estática en la versión 4.2.0 y superior, se puede usar el siguiente método de extensión (que se encuentra aquí en la clase AutoMapperExtensions):

// from http://stackoverflow.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, código como Mapper.FindTypeMapFor ya no funcionará, de ahí el uso del campo expression.TypeMap.

 16
Author: nick_w,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-29 07:33:22

Para Automapper 5.0 para omitir todas las propiedades no mapeadas, solo necesita poner

.Para todos los miembros(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 del objeto de salida y se omitirán todos los demás. Funciona como un encanto, parece que ya no necesitamos extensiones complicadas!

 13
Author: framerelay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-25 11:41:32

He actualizado la respuesta de Robert Schroeder para AutoMapper 4.2. Con configuraciones de mapeadores no estáticos, no podemos usar Mapper.GetAllTypeMaps(), pero el 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;
}
 10
Author: mrmashal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-13 05:54:53

¿Cómo preferirías especificar que ciertos miembros sean ignorados? ¿Hay una convención, o clase base, o atributo que le gustaría aplicar? Una vez que te metes en el negocio de especificar todas las asignaciones explícitamente, no estoy seguro de qué valor obtendrías de AutoMapper.

 8
Author: Jimmy Bogard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-05 12:31:23

Esta parece una vieja pregunta, pero pensé que publicaría mi respuesta para cualquier otra persona que se parezca a mí.

Uso ConstructUsing, inicializador de objetos junto con ForAllMembers ignore e. g

    Mapper.CreateMap<Source, Target>()
        .ConstructUsing(
            f =>
                new Target
                    {
                        PropVal1 = f.PropVal1,
                        PropObj2 = Map<PropObj2Class>(f.PropObj2),
                        PropVal4 = f.PropVal4
                    })
        .ForAllMembers(a => a.Ignore());
 7
Author: gm1886,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-07 22:33:00

La única infromación sobre ignorar a muchos de los miembros es este hilo - http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f . Creo que puedes usar el truco usado en ProvidingCommonBaseClassConfiguration para ignorar propiedades comunes para clases similares.
Y no hay información sobre la funcionalidad "Ignorar el resto". He mirado el código antes y me parece que será muy y muy difícil agregar tal funcionalidad. También puedes intente usar algún atributo y marque con él propiedades ignoradas y agregue algún código genérico / común para ignorar todas las propiedades marcadas.

 1
Author: zihotki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-05 10:27:50

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?

Puedes usar esta respuesta así dentro del Perfil ctor

this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));
 0
Author: j.loucao.silva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:03

Se puede utilizar para todos los miembros, que sobrescribir solo es necesario así

public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
        {
            expression.ForAllMembers(opt => opt.Ignore());
            return expression;
        }

Sea cuidadoso, ignorará todo, y si no agrega mapeo personalizado, ya se ignoran y no funcionarán

También, quiero decir, si usted tiene prueba unitaria para AutoMapper. Y prueba que todos los modelos con todas las propiedades mapeadas correctamente no debe usar dicho método de extensión

Debe escribir ignorados explícitamente

 0
Author: zxxc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-06 08:09:28

En la versión de 3.3.1 simplemente puede usar IgnoreAllPropertiesWithAnInaccessibleSetter() o IgnoreAllSourcePropertiesWithAnInaccessibleSetter() métodos.

 -1
Author: Ivan Kochurkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-21 13:13:47