Pergunta

Existe uma maneira de dizer AutoMapper para ignorar todas as propriedades, exceto os que estão explicitamente mapeadas?

Eu tenho aulas DTO externos que são susceptíveis de mudança do lado de fora e eu quero evitar especificando cada propriedade para ser ignorado explicitamente, desde a adição de novas propriedades vai quebrar a funcionalidade (causa exceções) ao tentar mapeá-los em meus próprios objetos .

Foi útil?

Solução

Este é um método de extensão Eu escrevi que ignora todas as propriedades não existentes no destino. Não tenho certeza se ele ainda vai ser útil como a questão é mais de dois anos de idade, mas eu corri para o mesmo problema ter que adicionar um monte de manual de Ignorar chamadas.

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

Atualizar : Aparentemente isso não funcionar corretamente se você tiver mapeamentos personalizados porque ele substitui-los. Eu acho que ainda poderia funcionar se chamada IgnoreAllNonExisting primeiro e depois os mapeamentos personalizados mais tarde.

schdr tem uma solução (como uma resposta a esta pergunta) que utiliza Mapper.GetAllTypeMaps() para descobrir quais propriedades são não mapeada e auto ignorá-los. Parece ser uma solução mais robusta para mim.

Outras dicas

Eu atualizei extensão do Can Gencer para não substituir quaisquer mapas existentes.

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

Pelo que eu entendi a pergunta era de que existem campos no destino que não têm um campo mapeado na fonte, que é por isso que você está procurando maneiras de ignorar esses campos não mapeados de destino.

Em vez de implementar e usar estes método de extensão que você poderia simplesmente usar

Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Source);  

Agora, o AutoMapper sabe que ele precisa só validar as que todos os campos de origem são mapeados, mas não o contrário.

Você também pode usar:

Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Destination);  

Eu tenho sido capaz de fazer isso da seguinte forma:

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:. Estou usando AutoMapper v.2.0

Versão 5.0.0-beta-1 de AutoMapper introduz o método de extensão ForAllOtherMembers então agora você pode fazer isso:

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

Esteja ciente de que há uma vantagem para mapear explicitamente cada propriedade como você nunca vai ter problemas de mapeamento falha silenciosamente que surgem quando você se esqueça de mapear uma propriedade.

Talvez no seu caso pode ser sábio para ignorar todos os outros membros e adicionar um TODO para voltar e fazer essas explícita após a frequência de alterações a esta classe se acalmar.

A partir de AutoMapper 5.0, a propriedade .TypeMap em IMappingExpression se foi, o que significa que a solução 4.2 obras não mais. Eu criei uma solução que utiliza a funcionalidade original, mas com uma sintaxe 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>();
}

Implementação:

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

Tem havido alguns anos desde que a pergunta foi feita, mas este método de extensão parece mais limpo para mim, usando a versão atual do 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;
}

Para aqueles que estão usando o API non-static na versão 4.2.0 e acima, o seguinte método de extensão (encontrado aqui na classe AutoMapperExtensions ) pode ser usado:

// 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;
}

O importante aqui é que uma vez que a API estática é removida, o código, como Mapper.FindTypeMapFor não funcionará mais, daí o uso do campo expression.TypeMap.

Para AutoMapper 5.0, a fim de ignorar todas as propriedades não mapeados Você só precisa colocar

.ForAllOtherMembers (x => x.Ignore ());

no final do seu perfil.

Por exemplo:

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

Neste caso único campo Id para objeto de saída será resolvido todos os outros serão ignorados. Funciona como um encanto, parece não precisamos de mais nenhum extensões complicado!

Eu atualizei a resposta de Robert Schroeder para AutoMapper 4.2. Com configurações mapeador não-estáticos, não podemos usar Mapper.GetAllTypeMaps(), mas o expression tem uma referência para o TypeMap necessário:

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;
}

Como você prefere para especificar que certos membros ser ignorado? Existe uma convenção, ou classe base, ou atributo que você gostaria de aplicar? Uma vez que você entrar no negócio de especificar todos os mapeamentos explicitamente, eu não tenho certeza do que o valor que você sairia AutoMapper.

Isto parece uma questão antiga, mas pensei que eu ia postar minha resposta para qualquer outra pessoa olhando como se eu fosse.

Eu uso ConstructUsing, de inicialização do objeto juntamente com ForAllMembers ignorar por exemplo

    Mapper.CreateMap<Source, Target>()
        .ConstructUsing(
            f =>
                new Target
                    {
                        PropVal1 = f.PropVal1,
                        PropObj2 = Map<PropObj2Class>(f.PropObj2),
                        PropVal4 = f.PropVal4
                    })
        .ForAllMembers(a => a.Ignore());

A única infromation sobre ignorar muitos dos membros é esta discussão - http : //groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f . Eu acho que você pode usar o truque usado em ProvidingCommonBaseClassConfiguration ignorar propriedades comuns para classes semelhantes.
E não há informações sobre o "Ignore o resto" funcionalidade. Eu olhei o código antes e parece-me que será muito e muito difícil para adicionar essa funcionalidade. Além disso, você pode tentar usar algum atributo e marca com ele propriedades ignorados e adicionar algum código genérico / comum para ignorar todas as propriedades marcadas.

Eu sei que isto é uma questão de idade, mas @jmoerdyk na sua pergunta:

Como você usar isso em uma expressão CreateMap encadeada () em um perfil?

Você pode usar este resposta como esta dentro do perfil ctor

this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));

Você pode usar ForAllMembers, do que de substituição só precisava como esta

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

Tenha cuidado, ele irá ignorar tudo, e se você não irá adicionar mapeamento personalizado, eles já estão ignorar e não vai funcionar

Além disso, eu quero dizer, se você tem teste de unidade para AutoMapper. E você testar que todos os modelos com todas as propriedades mapeadas corretamente você não deve usar esse método de extensão

você deve escrever ignorar de forma explícita

Na versão 3.3.1 você simplesmente pode usar métodos IgnoreAllPropertiesWithAnInaccessibleSetter() ou IgnoreAllSourcePropertiesWithAnInaccessibleSetter().

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top