AutoMapper: "Игнорировать остальных"?

Есть ли способ сказать AutoMapper игнорировать все свойства, кроме тех, которые явно отображаются?

У меня есть внешние классы DTO, которые могут меняться извне, и я хочу, чтобы не указывать, что каждое свойство игнорируется явно, поскольку добавление новых свойств приведет к нарушению функциональности (вызывают исключения) при попытке сопоставить их с моими собственными объектами.

Ответ 1

Это метод расширения, который я написал, который игнорирует все несуществующие свойства в месте назначения. Не уверен, что он по-прежнему будет полезен, так как вопрос больше двух лет, но я столкнулся с тем же вопросом, который должен был добавить много ручных вызовов Ignore.

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

Использование:

Mapper.CreateMap<SourceType, DestinationType>()
                .IgnoreAllNonExisting();

UPDATE. По-видимому, это не работает правильно, если у вас есть настраиваемые сопоставления, потому что они перезаписывают их. Я думаю, он все равно может работать, если сначала вызовет IgnoreAllNonExisting, а затем пользовательские сопоставления.

schdr имеет решение (в качестве ответа на этот вопрос), которое использует Mapper.GetAllTypeMaps(), чтобы выяснить, какие свойства не отображаются и автоматически игнорировать их. Кажется, это более надежное решение.

Ответ 2

Я обновил расширение Can Gencer, чтобы не перезаписывать существующие карты.

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

Использование:

Mapper.CreateMap<SourceType, DestinationType>()
                .ForMember(prop => x.Property, opt => opt.MapFrom(src => src.OtherProperty))
                .IgnoreAllNonExisting();

Ответ 3

Я смог сделать это следующим образом:

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*/);
...

Примечание. Я использую AutoMapper v.2.0.

Ответ 4

Из того, что я понял, вопрос заключался в том, что в месте назначения есть поля, у которых нет поля отображения в источнике, поэтому вы ищите способы игнорировать те немаркированные поля назначения.

Вместо внедрения и использования этого метода расширения вы можете просто использовать

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

Теперь automapper знает, что ему нужно только проверить, что все поля источника отображаются, но не наоборот.

Ответ 5

Как и в AutoMapper 5.0, свойство .TypeMap на IMappingExpression исчезло, что означает, что решение 4.2 больше не работает. Я создал решение, которое использует оригинальную функциональность, но с другим синтаксисом:

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

Реализация:

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

Ответ 6

Прошло несколько лет с тех пор, как был задан вопрос, но этот метод расширения кажется мне более чистым, используя текущую версию 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;
}

Ответ 7

Версия 5.0.0-beta-1 в AutoMapper вводит метод расширения ForAllOtherMembers, поэтому теперь вы можете сделать это:

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

Имейте в виду, что есть преимущество в явном сопоставлении каждого свойства, так как вы никогда не получите проблем с отображением, которое будет терпеть неудачу, возникающие, когда вы забудете отобразить свойство.

Возможно, в вашем случае было бы разумно игнорировать все остальные члены и добавить TODO, чтобы вернуться и сделать это явным после того, как частота изменений в этом классе успокоится.

Ответ 8

Для тех, кто использует нестатический API в версии 4.2.0 и выше, следующий метод расширения (найденный здесь в классе 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;
}

Важно то, что после удаления статического API код, такой как Mapper.FindTypeMapFor, больше не будет работать, поэтому использование поля expression.TypeMap.

Ответ 9

Для Automapper 5.0, чтобы пропустить все неотобранные свойства, вам просто нужно поставить

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

в конце вашего профиля.

Например:

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

В этом случае будет разрешено только поле Id для выходного объекта, все остальные будут пропущены. Работает как шарм, кажется, нам больше не нужны какие-либо сложные расширения!

Ответ 10

Я обновил ответ Роберта Шредера для AutoMapper 4.2. При использовании нестатических конфигураций сопоставления мы не можем использовать Mapper.GetAllTypeMaps(), но expression имеет ссылку на требуемый TypeMap:

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

Ответ 11

Как вы предпочитаете указывать, что некоторые члены игнорируются? Существует ли соглашение или базовый класс или атрибут, который вы хотите применить? Как только вы входите в бизнес, указав все сопоставления явно, я не уверен, какое значение вы бы выбрали из AutoMapper.

Ответ 12

Это кажется старым вопросом, но я думал, что отправлю свой ответ всем, кто был похож на меня.

Я использую ConstructUsing, инициализатор объекта в сочетании с игнорируемыми символами ForAllMM, например,

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

Ответ 13

Единственная информация об игнорировании многих членов - это поток - http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f. Я думаю, вы можете использовать трюк, используемый в ProvidingCommonBaseClassConfiguration, чтобы игнорировать общие свойства для аналогичных классов.
И нет никакой информации об функциональности "Игнорировать остальных". Я уже смотрел код раньше, и мне кажется, что будет очень и очень сложно добавить такую ​​функциональность. Также вы можете попробовать использовать некоторый атрибут и пометить с ним игнорируемые свойства и добавить общий или общий код, чтобы игнорировать все отмеченные свойства.

Ответ 14

В версии 3.3.1 вы просто можете использовать методы IgnoreAllPropertiesWithAnInaccessibleSetter() или IgnoreAllSourcePropertiesWithAnInaccessibleSetter().

Ответ 15

Вы можете использовать ForAllMembers, чем перезаписывать только необходимые как это

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

Будьте осторожны, он будет игнорировать все, и если вы не добавите настраиваемое сопоставление, они уже игнорируют и не будут работать

также, я хочу сказать, если у вас есть unit test для AutoMapper. И вы проверяете, что все модели со всеми свойствами отображаются правильно, вы не должны использовать такой метод расширения

вы должны явно писать игнорировать

Ответ 16

Я знаю, что это старый вопрос, но @jmoerdyk в вашем вопросе:

Как вы могли бы использовать это в выраженном в цепочке выражении CreateMap() в профиле?

вы можете использовать этот ответ, как это показано в профиле ctor

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