Pull to refresh

Comments 34

Вашему проекту это подходит?

Дааа! Безусловно! Ты задолбаешься все эти dto в бизнес модели мапить и обратно! А без dto не будет версионирования API. К тому же dto у какого-нибудь гугла "смотришь и плачешь". Один их универсальный OBJECT чего стоит ;)

Но бизнес логику пихать в маппинг нельзя, это просто ошибка

UFO just landed and posted this here

т.е. брём конфиг автомаппера и генерируем код по его правилам.

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

UFO just landed and posted this here

Mожете поделиться ссылкой, пожалуйста?

UFO just landed and posted this here

Библиотека Mapster поддерживает кодогенерацию. Да и даже без режима кодогенерации все равно работает чуточку быстрее, чем AutoMapper.

Ничего сложного если слезать не моментально.


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

UFO just landed and posted this here

Наблюдал на примере двух систем и десятка компонентов, бизнес-логика всегда (рано или поздно) просачивалась в конфигурацию автомаппера, и потом поддержка становилась ещё болезненнее. Это какое-то невообразимое природное свойство подобных инструментов, притягивать бизнес-логику, как например: маппинг разных енумов, значения по-умолчанию, flatten-unflatten (что вообще заход на поле доменного представления). А в самый неудобный момент это становится внезапным сюрпризом.

UFO just landed and posted this here

Интересный пример! Кстати про первый снимает статья даже была, Fluent Generics называлась. Не думал, что встречу ещё где-то этот подход

https://habr.com/ru/post/666244/

В первом примере ещё надо придумать как этот маппинг мокать для юнит тестов.

Я не могу понять одного, почему библиотека или ее автор решает за меня как ее использовать? Если есть инструмент для маппинга, то его домен это маппинг и основная задача - предоставить апи для решения задач маппинга. Вместо этого создатели автомаппера гнут свою линию, причем доходит до смешного: в одном ответе на стековерфлоу они советуют юзать какой-то новый метод, а уже в другом говорят что это плохая идея, а метод мы удалили навсегда. И эти парни не знают слов deprecated, obsolete и тп. Мы художники - мы так видим, а вы переписывайте как хотите ваши маппинги - напрасная трата человеко-часов. Мало того, они как бы дают вам возможность вернуть часть функционала, если очень надо, но код примеров еще надо найти в каком-то из обсуждений и самое главное - этот код бажный, какое-то издевательство. Этого достаточно, чтобы начать обходить эту либу стороной, неизвестно что они решат удалить или запретить завтра, а переписывать сотни мапингов нет никакого желания.

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

И что вы думаете, буквально на днях они снова сломали код, ProjectTo перестал работать с EF6 LINQ - без всякой вообще причины, эти люди неизлечимо больные. Вместо Expression.Constant() стали использовать Expression.Default() который старые бэкенды не умеют. Зачем, зачем так делать??

Разумеется, они ответили, что якобы теперь поддерживают только EF Core - facepalm!

Сам автомаппер не сильно нравится.

Но очень помогает в некоторых сценариях автомаппер.коллекшн

Берешь коллекцию из внешнего источника, из своей БД и мержишь их. Всего пара строчек автомаппера и это работает, здорово! А вот писать самому такое - лень =) Но, может есть инструменты хорошие, а я просто не в курсе?

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

А для линка придётся описывать каждое конкретное поле с вложенной коллекцией. Муторно, но более правильно, наверное.

Я иногда просто создаю IConvert<A,B>, минус в том что приходиться внедрять вложенные маппинги в конструктор, но то что код явный и на руках и его удобно отладить большой плюс. Появляется даже гибкость в особых случаях. И можно использовать языковые фичи которые в случает ioc подходов невозможны, когда фреймворк сам через рефлексии мапит.

Можно создать маппер, который будет хранить всю эту конфигурацию по ключу типА, типБ.

На старте приложения провести исследование сборок на наличие реализаций вашего интерфейса IConvert и наполнить словарь маппера.

services.UseMyMapper();
//или
services.UseMyMapper(assembly1, assembly2);
//ну а с реализацией, надеюсь, сами разберетесь.

Хотя, ваш вариант проще тестировать.

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

И вы правильно заметили про тестирование, простой интерфейс гораздо проще замокать.

Что значит поддерживать рефлексию? Там 1 раз настроить поиск реализаций интерфейсов в сборках и все. Чё ее поддерживать то.

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

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

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

Ну код требует правок по 2 причинам.
1. Баги
2. Доработки, новый функционал.

1. пункт в данном случае решается тестами и забывается.
2. Ооочень редко вам это понадобится.

Да и рефлекшн не повышает порог входа. Джуны его обожают ))

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

По поводу оценки "редко" или "не редко" я вам тоже могу сказать что всё, что может пойти не так, пойдет не так, по закону подлости, эти суждения конкретно тут, конкретно к пованивающему коду на рефлексии применять не стоит.

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

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

Объясните тогда по другому пожалуйста

Оставлю пару замечаний.

Обманчивый статический анализ

Беспомощность навигации кода

Настраивать маппер лучше явно. Проблема исчезнет.

Решением проблемы могло бы быть написание явных конфигураций отображения. Однако, нужен ли весь этот комплексный механизм, основанный на рефлексии, в таком случае?

Нужен. Во первых, это не рефлексия, а Expression, которые компилируются один раз при использовании. После компиляции Expression получаются делегаты. Так что за скорость беспокоиться тут не стоит.

А самое главное, все конфигурации маппинга можно расположить в проекте там, где это удобно. Если вы сторонник DDD, то вам, вероятно, не нужен автомаппер. Хотя, я не уверен, что все сущности должны иметь прямую ссылку к другим сущностям. Слои там и все такое. Так что автомаппер тут выступает неким медиатором.

Кстати, конфигурации можно настроить так, чтобы они требовали явного указания всех свойств. А валидность конфигурации после этого можно проверить в юнит тестах. При этом статический анализатор для тонких сущностей можно и отключить (именно в разрезе использования свойств).

Ну и еще, автомаппер можно использовать для автоматической проекции из Queryable. То есть можно использовать при запросах к БД через ORM.

Блеск. Поделился опытом использования и некоторыми особенностями под капотом - заминусили. Кто то даже не поленился и минуснул карму. Могли просто написать с чем не согласны :)

Автомаппер может стать как источником ошибок, как автор справедливо заметил, из-за неявного маппингда, так и спасением.
Если на проекте существует четкий конвенционный маппинг, model->dto где в принципе поля различаются только их наличием или отсутствием, то автомаппер решает много кейсов, где программист добавил новое поле в модель и банально забыл смаппить или забыл добавить его в dto. Тесты в данном случае не помогут и будут предательски зелеными, так как тесты тоже нужно обновлять после обновления модели. Автомаппер не серебряная пуля, но имеет как кучу достоинств - избавление от мартышкиного перекладывания моделей, так и недостатков - необходимость изучить инструмент и понимать как он работает и в каких случаях не сработает (как в примере с рекордами)

По поводу некоторых замечаний автора -

Перформанс: Тут речь идет о наносекундах. Справедливо ли о них говорить в контексте проекта не High Load? Думаю нет. Там вопросы будут стоять совсем другие.

Usage: Да, использование поля мы не увидим, зато легко поднятся на уровень dto или модели и нажать ctrl + t и увидеть заветный Map<>.

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

По личному опыту, использовал автомаппер на 10+ проектах и использовал ручной маппинг на LowLatency проекте. Все же конфигурация автомаппера нравится больше. Тестировать нужно оба подхода, но автомаппер экономит банально много времени на бесмыссленном перекладывании модели в дото и обратно.

Sign up to leave a comment.

Articles