AutoMapper: «Игнорировать все остальное»?

206

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

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

Игорь Брейц
источник
1
с помощью ValueInjecter valueinjecter.codeplex.com/documentation вы создаете ValueInjection, у которого есть алгоритм сопоставления и сопоставление между конкретными свойствами, и им нет дела до остальных свойств
Omu
24
Для тех, кто использует Automapper> версия 5, пропустите, чтобы увидеть подробные ответы.ForAllOtherMembers(opts => opts.Ignore())
Джек Уклея
@Schneider ".ForAllOtherMembers (opts => opts.Ignore ())" отличается от расширения "IgnoreAllNonExisting" здесь, главное отличие состоит в том, что если вы не настроили свойство явно, с помощью ".ForAllOtherMembers (opts => opts.Ignore ( )) "вы ничего не получите на карту. используйте «IgnoreAllNonExisting» без свойства config в явном виде, вы все равно получите сопоставленное свойство (свойства с тем же именем) со значением.
Дракон
Да. ForAllOtherMembers - это ответ. Ответы IgnoreUnmapped ничего не делают, за исключением того, что config-valid-assert проходит, потому что не отображенные члены в любом случае игнорируются.
N73k
Стоит отметить, что при этом вы явно скрываете потенциально важные или важные изменения в отображаемых классах. Наличие явных сопоставлений для каждого свойства приведет к тому, что при каждом изменении сопоставленного класса у вас останется неработающий тест, что заставит вас правильно его оценить. (Учитывая, что у вас есть тест, выполняющий AssertConfigurationIsValid()вызов) Из-за этого я считаю «Игнорировать остальное» антипаттерном.
Арве Систад

Ответы:

83

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

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

Может Генсер
источник
Я не использовал AutoMapper в течение некоторого времени, но я приму ваш ответ, если он работает для вас :).
Игорь Брейц
2
Спасибо!! Я нашел это очень удобным. Игнорирование свойств по отдельности побеждало цель использования autopper в моей ситуации.
Даниэль Робинсон
См. Следующий ответ для того, у которого нет проблемы перезаписи
Джейсон Койн
3
Этот метод должен быть в собственном коде autoMapper! Очень хорошо, спасибо!
Фелипе Ориани
2
К вашему сведению, сам Джимми (автор AutoMapper) прокомментировал ниже, что ответ @ nazim верен для версии 5+
Worthy7
244

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

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

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

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

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

Mapper.CreateMap<sourceModel, destinationModel>(MemberList.Destination);  
Назим Хафиз
источник
10
Этот ответ должен иметь больше голосов, может быть даже помечен как ответ. Это решило мою проблему и точно так же MemberList.Destinationрешило бы проблему ops.
Тедд Хансен
1
Это не будет работать, если вы хотите игнорировать несколько свойств как источника, так и места назначения :)
RealWillyWoka
62
Для всех, кто придет позже, ЭТО ПРАВИЛЬНЫЙ ОТВЕТ НА 5.0
Джимми Богард
3
выглядит изящно, но у меня не получилось .. я попробовал Source и Destination, но он продолжает жаловаться на один и тот же объект свойства, отсутствующий на карте
Sonic Soul
1
Использование 6.0.2, и это не работает. Любое свойство, которое не сопоставлено от места назначения до источника, перезаписывает свойства в источнике нулями и 0. Кроме того, код не дает понять, что вы делаете, особенно если вы работаете в команде. Вот почему мне очень не нравится этот код, и поэтому я предпочитаю избранные слова, такие как предлагаемый ответ «IgnoreAllNonExisting»
sksallaj
222

Я обновил расширение 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();
Роберт Шредер
источник
4
+1, спасибо за размещение этого решения. Мне понадобилось несколько часов, чтобы понять причудливую ошибку, когда я использую решение в goo.gl/rG7SL , пока я снова не наткнулся на этот пост.
Нордин
3
Я рекомендую метод Йоханба ниже по этому поводу. Есть некоторые угловые случаи, которые это не работает для этого, кажется.
Джон Баркер,
3
Можно ли это сделать в AutoMapper 4.2? (Не Mapper.GetAllTypeMaps()рекомендуется)
Мрмашал
14
Для версии AutoMapper 5+ просто замените Mapper.GetAllTypeMaps()на Mapper.Configuration.GetAllTypeMaps(). Вот ссылка github.com/AutoMapper/AutoMapper/issues/1252
Сергей Григорьевич
5
Для новых людей, читающих это. Этот ответ предназначен для AutoMapper 2, и на момент написания этого комментария мы находимся на версии 6. Это хак и гораздо более чистый способ - использовать перечисление MemberList. Смотрите выпуск Github 1839 и лучшее решение. github.com/AutoMapper/AutoMapper/issues/1839 Итак, пример: stackoverflow.com/a/31182390/3850405
Ogglas
83

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

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.

Yohanb
источник
4
Спасибо большое! Работает как часы. сначала я попытался объединить вызовы, но ForAllMembers просто возвращает void :(. Не было очевидно, что предыдущий IgnoreAll можно будет изменить позже.
SeriousM
5
Мне тоже не нравится этот способ ... если у вас есть 50 участников, и вы хотите игнорировать 25 ... тогда какой смысл в автомате, если вы все еще должны игнорировать 25 участников. Если имена совпадают, и есть свойства, которые не совпадают ... почему бы не дать понять автомату, чтобы они не совпадали с несопоставленными свойствами и путем передачи всей типизации?
Сксаллай
71

Версия 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());

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

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

ajbeaven
источник
3
Удивительно, но до версии 5. Посмотрите, сколько голосов "за" и попыток ответить на этот вопрос ... что-то не так с управлением Automapper, интересно?
Джек Уклея
Спасибо вам за это, мне потребовалось некоторое время, чтобы прокрутить вниз, но это работает отлично.
cobolstinks
2
Вы даже можете сначала поставить строку ForAllOtherMembers, и все будет работать одинаково, что хорошо, если у вас есть какая-то конфигурация базового класса.
N73k
Это сейчас предпочтительный подход. Интересно, сможет ли ОП изменить принятый ответ?
Чейз
1
Есть ли эквивалент игнорировать свойства в исходном объекте? Что то типа ForAllOtherSourceMembers?
SuperJMN
44

Начиная с AutoMapper 5.0, .TypeMapсвойство on 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));
    }
}
Ричард
источник
3
Как бы вы использовали это в цепочечном CreateMap<TSource,TDest>()выражении в Profile?
Jmoerdyk
2
Спасибо за это. Метод GetUnmappedPropertyNames возвращает все несопоставленные имена свойств, как в источнике, так и в месте назначения, что, по-видимому, разрывается на обратной карте, поэтому мне пришлось внести небольшое изменение в IgnoreUnmapped, чтобы проверить, было ли свойство без отображения в источнике или месте назначения, и игнорировать соответственно. Вот скрипка, демонстрирующая проблему и обновление: dotnetfiddle.net/vkRGJv
Mun
1
Я обновил свой ответ, чтобы включить ваши выводы - я не использую сопоставления источников, поэтому не сталкивался с этим! Спасибо.
Ричард
1
Это не работает на PCL без отражения, GetProperty (propName) не существует.
Джордж Таскос
Я не вижу, как это решение вопроса, или как это вообще что-то делает. Несопоставленные свойства уже будут игнорироваться, потому что они не сопоставлены . На плакате говорилось: «Как вы игнорируете реквизиты, если они явно не отображаются». Это означает, что если у меня есть Src.MyProp и Dest.MyProp, это отображение следует игнорировать, если не было явного вызова MapFrom & ForMember для MyProp. Таким образом, отображение по умолчанию необходимо игнорировать. Единственное, что делает это решение, это заставляет вещь config-valid-assert проходить - что вам в любом случае не нужно для работы отображения.
N73k
17

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

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

nick_w
источник
7
По состоянию на 5.0 expression.TypeMapбольше не доступен. Вот мое решение для 5.0
Ричард
Я должен был использовать, public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)чтобы исправить проблемы с типом.
Ник М
16

Для 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;
}
mrmashal
источник
Не работает в AutoMapper 5.0. Свойство .TypeMap для IMappingExpression недоступно. Для версии 5.+ см. Расширения в ответе Ричарда
Майкл Фрейдгейм
Работает с AM 4.2
Leszek P
8

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

Джимми Богард
источник
Джимми, у тебя есть точка зрения о явности. Что касается того, как добиться этого наиболее элегантным способом: базовые классы и атрибуты не будут работать в этой ситуации, поскольку целевые классы на самом деле не находятся под моим контролем - они автоматически генерируются из контракта данных XSD, поэтому можно было бы вручную редактировать этот код после каждого цикла генерации. Я думаю, что решение зависит от конкретного случая. Может быть, свободный интерфейс, похожий на тот, который обеспечивает Виндзорский замок для выбора компонентов для регистрации в контейнере, может быть решением?
Игорь Брейц
Ах, это имеет больше смысла сейчас. Это интересная функция, я посмотрю на нее в периоде 2.1.
Джимми Богард
2
Как насчет просто настраиваемого значения, где вы можете «игнорировать» все несуществующие поля.
Рикардо Санчес
6
Это не ответ на вопрос.
user2864740
Привет Джимми, ты автор, правильно? Я хотел бы иметь возможность игнорировать все несуществующие свойства, являющиеся поведением по умолчанию (может управляться флагом). Кроме того, у меня странная ошибка от AutoMapper, которую я не могу понять. Это не дает мне никакой специфики.
Наоми
7

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

Я использую 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());
gm1886
источник
1

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

zihotki
источник
1
Возможно, одним из способов было бы использование метода ForAllMembers и реализация моего собственного IMemberConfigurationExpression, которое получает строку, содержащую имена свойств этих свойств, которые не следует игнорировать, а затем проходит через все остальные и вызывает Ignore (). Просто идея, я не уверен, что это сработает.
Игорь Брейц
Да, это тоже может работать, но этот метод более сложный, чем использование атрибутов, но он предлагает большую гибкость. Жаль, что нет серебряной пули :(.
Зихотки
1

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

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

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

this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));
j.loucao.silva
источник
0

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

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

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

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

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

Анатолий Кламер
источник
-1

Текущее (версия 9) решение для игнорирования свойств, которые не существуют в типе назначения, состоит в том, чтобы создать перевернутое отображение и обратить его вспять:

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<TheActualDestinationType, TheActualSourceType>().ReverseMap();
});
Simopaa
источник
-2

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

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