AutoMapper:"игнорировать остальное"?


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

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

16 160

16 ответов:

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

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

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

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

я обновил расширение 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();

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

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

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

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

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

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 В. 2.0.

начиная с 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));
    }
}

версия 5.0.0-бета-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 чтобы вернуться и сделать их явными после частоты изменений в этом классе располагаться.

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

для тех, кто использует не-static 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 поле.

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

.ForAllOtherMembers (x= > 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 для выходного объекта, все остальные будут пропущены. Работает как шарм, кажется, нам больше не нужны никакие хитрые расширения!

я обновил ответ Роберта Шредера для AutoMapper 4.2. С нестатическими конфигурациями mapper мы не можем использовать 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;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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