AutoMapper: "Ігноруйте решту"?


205

Чи є спосіб сказати AutoMapper ігнорувати всі властивості, крім тих, які відображені явно?

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


1
за допомогою ValueInjecter valueinjecter.codeplex.com/documentation ви створюєте ValueInjections, які мають алгоритм зіставлення та відображення між певними властивостями, і вони не переймаються рештою властивостей
Omu,

24
Для тих, хто використовує Automapper> версія 5, пропустіть униз, щоб побачити відповіді з деталізацією.ForAllOtherMembers(opts => opts.Ignore())
Jack Ukleja

@Schneider ".ForAllOtherMembers (opts => opts.Ignore ())" відрізняється від розширення "IgnoreAllNonExisting" тут, головна відмінність полягає в тому, якщо ви не налаштували властивість явно, з ".ForAllOtherMembers (opts => opts.Ignore ( )) "у вас нічого не буде відображено. використовуйте "IgnoreAllNonExisting" без властивості конфігурації явно, ви все одно отримаєте відображену деяку властивість (властивості з тим самим іменем) зі значенням.
Дракон

Так. Відповідь 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
Дякую!! Я вважав це дуже зручним. Ігнорування властивостей індивідуально перемагало мету використання автоматизатора в моїй ситуації.
Даніель Робінсон

Подивіться наступну відповідь на те, у кого немає проблеми з перезаписом
Джейсон Койн

3
Цей метод повинен бути на нашому коді autoMapper! Дуже приємно, дякую!
Феліпе Оріані

2
FYI, сам Джиммі (письменник AutoMapper) прокоментував нижче, що відповідь @ nazim правильна для версії 5+
Варто7,

244

З того, що я зрозумів, питання полягає в тому, що в пункті призначення є поля, які не мають відображеного поля в джерелі, і саме тому ви шукаєте способи ігнорувати ці не відображені поля призначення.

Замість реалізації та використання цих методів розширення ви можете просто використовувати

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

Тепер автомат знає, що йому потрібно лише перевірити, що всі вихідні поля відображаються, але не навпаки.

Ви також можете використовувати:

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

10
Ця відповідь повинна мати більше результатів, можливо навіть позначена як відповідь. Це вирішило мою проблему і так само MemberList.Destinationвирішило б проблему ops.
Тедд Хансен

1
Це не спрацює, якщо ви хочете ігнорувати небагато ресурсів як у вихідному, так і в
кінцевому

62
Для всіх, хто прийде пізніше, ЦЕ ПРАВИЛЬНА ВІДПОВІДЬ 5.0
Джиммі Богард

3
виглядає вишукано, але не спрацювало для мене .. Я спробував Джерело та Місце призначення, але він продовжує скаржитися на той самий об’єкт власності, на якому відсутня карта
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/isissue/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.


4
Дуже дякую! це працює як шарм. Я спробував спочатку ланцюг дзвінків, але ForAllMembers просто повертає недійсність :(. Не було очевидно, що попередній IgnoreAll може бути змінений пізніше.
SeriousM

5
Мені це також не подобається .. якщо у вас є 50 членів, і ви хочете ігнорувати 25 .. тоді який сенс автоматника, якщо ви все-таки повинні ігнорувати 25 членів. Якщо імена збігаються, а є властивості, які не збігаються .. чому б не дати зрозуміти, щоб сказати autompper, щоб вони не відповідали невлаштованим властивостям, а також передавши всі введення тексту?
sksallaj

71

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

Будьте в курсі, що є користь явного відображення кожної власності, оскільки у вас ніколи не виникнуть проблеми з безвідмовним картографуванням, які виникають, коли ви забудете промальовувати об’єкт.

Можливо, у вашому випадку може бути розумним ігнорувати всіх інших членів і додавати a, TODOщоб повернутися, і зробити це явним після того, як частота змін цього класу вляжеться.


3
Дивно, що це зайняло до версії 5. Подивіться, скільки голосів і спроб відповіді на це питання ... щось не так у керуванні Automapper мені цікаво?
Джек Уклея

Дякую за це, у мене пройшло деякий час, щоб прокрутити вниз, але це, але це працює чудово.
cobolstinks

2
Ви навіть можете поставити рядок ForAllOtherMembers першим, і все буде працювати так само, що добре, якщо у вас є якась конфігурація базового класу.
N73k

Зараз це кращий підхід. Цікаво, чи може ОП змінити прийняту відповідь?
Чейз Флорелл

1
Чи є еквівалент ігнорування властивостей у вихідному об'єкті? Щось схоже ForAllOtherSourceMembers?
SuperJMN

44

Станом на 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));
    }
}

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

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поля.


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

Не працює в AutoMapper 5.0. Властивість .TypeMap в IMappingExpression недоступна. Для версії 5. + див. Розширення у відповіді Річарда
Майкл Фрейджім

Працює з AM 4.2
Лешек P

8

Як ви хочете вказати, що певні члени ігноруються? Чи є конвенція, або базовий клас, або атрибут, який ви хочете застосувати? Після того, як ви ввійдете в справу чіткого визначення всіх відображень, я не знаю, яке значення ви отримаєте з AutoMapper.


Джіммі, ти маєш думку про явність. Щодо способу досягнення цього найелегантнішим чином: базові класи та атрибути не працюватимуть у цій ситуації, оскільки цільові класи насправді не під моїм контролем - вони автоматично генеруються з контракту даних XSD, тому вручну редагувати цей код після кожного циклу покоління. Я думаю, що рішення залежить від конкретного випадку. Можливо, вільний інтерфейс, подібний до того, що віндзорський замок, передбачає вибір компонентів для реєстрації в контейнері?
Ігор Брейц

А, це має більше сенсу зараз. Це цікава особливість, я дивлюся на цю в часовій рамці 2.1.
Джиммі Богард

2
Як щодо того, щоб просто мати налаштоване значення, де можна «ігнорувати» всі неіснуючі поля.
Рікардо Санчес

6
Це не відповідь на запитання.
користувач2864740

Привіт Джиммі, ти автор, правда? Мені б хотілося ігнорувати всі неіснуючі властивості, які є поведінкою за замовчуванням (можливо, вони керуються прапором). Крім того, у мене виникає дивна помилка 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());

1

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


1
Можливо, одним із способів було б скористатися методом ForAllMembers та реалізувати власний IMemberConfigurationExpression, який отримує рядок, що містить назви властивостей тих властивостей, які не слід ігнорувати, а потім пройти решту з них та викликати Ignore (). Просто ідея, я не впевнений, чи спрацює це.
Ігор Брейц

Так, це також може працювати, але цей метод є більш складним, ніж використання атрибутів, але він пропонує більшу гнучкість. Шкода, що немає срібної кулі :(.
zihotki

1

Я знаю, що це старе запитання, але @jmoerdyk у вашому запитанні:

Як би ви використовували це в ланцюговому виразі CreateMap () у профілі?

Ви можете використовувати цю відповідь, як це всередині ctor профілю

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

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

-2

У версії 3.3.1 ви просто можете використовувати IgnoreAllPropertiesWithAnInaccessibleSetter()або IgnoreAllSourcePropertiesWithAnInaccessibleSetter()методи.


6
Це не працює на запитання оригінального афіші. Ці методи ігнорують лише захищені або приватні властивості, а не властивості, які відсутні у джерела, але є у типі призначення.
Дан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.