AutoMapper: «Игнорировать все остальное»? - PullRequest
187 голосов
/ 05 июня 2009

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

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

Ответы [ 16 ]

218 голосов
/ 25 июня 2011

Я обновил расширение 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();
189 голосов
/ 02 июля 2015

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

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

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

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

Вы также можете использовать:

Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Destination);  
82 голосов
/ 08 декабря 2011

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

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.

78 голосов
/ 08 июня 2011

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

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(), чтобы выяснить, какие свойства не сопоставлены, и автоматически игнорировать их. Мне кажется, это более надежное решение.

53 голосов
/ 23 августа 2016

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

42 голосов
/ 28 июня 2016

Начиная с 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));
    }
}
17 голосов
/ 05 ноября 2014

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

Для тех, кто использует нестатический API в версии 4.2.0 и выше, можно использовать следующий метод расширения (найден здесь в классе AutoMapperExtensions) :

// from /787589/automapper-ignorirovat-vse-ostalnoe#787599
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.

15 голосов
/ 25 августа 2016

Для 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 голосов
/ 13 февраля 2016

Я обновил ответ Роберта Шредера для 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...