Comments 24
Можно ли добавить в бенчмарк маппинг ручками?
Спасибо за статью. Мое субъективное мнение - мне больше нравятся кодогенераторы, во первых можно поставить точку останова и посмотреть, что там происходит, во вторых можно взять этот код за основу и дописать более сложную логику с вычисляемыми полями. Я ранее пользовался mappinggenerator.net для такого, посмотрю вариант Mapster с кодогенерацией. А кодогенерация работает в связке с EF? Чтобы не приходилось вручную писать методы расширения:
public static IQueryable<UserDto> MapUserToDTO(this IQueryable<User> users)
return users.Select(u => new UserDto
{
Id = u.Id,
Email = u.Email,
Name = u.Name
});
}
var user = await ctx.Users.MapUserToDTO().ToListAsync()
Mapperly, который самый быстрый, полностью основан на кодогенерации. Решает все проблемы маппинга в 3 строки и не использует DI. И точно умеет маппить IQueriable, ничего вручную писать не нужно. В принципе не люблю мапперы и стараюсь избегать, но когда надо, по мне мапперли безальтернативен.
Насколько я смог раскопать, кодогенерацию используют только в связке с интерфейсами для создания «классических» мапперов. Возможно, я что-то упустил, но могу еще посоветовать другую статью, где некоторые аспекты освещены более подробно: https://code-maze.com/mapster-aspnetcore-introduction/
Исходя из личного опыта, мапперы позволяют выстрелить себе в ногу в самые неподходящие и болезненные моменты. Переименование филда, миграция данных или несоответствие типов приводят к неожиданным результатам или даже уязвимостям. Разработчику придется выучить ещё одну библиотеку и как она работает + тесты. А во время PR ревью нужно всем объяснять.
Для меня новый маппер номер 1 это GitHub Copilot или его альтернатива. Он генерирует статический маппинг, который я должен просто проверить глазами. Никаких сайд эффектов при добавлении новых филдов. Все зависимости находятся компилятором. Порог вхождения минимальный. Код ревью можно сделать даже в vim/nano. Так как маппинг получается прозрачным, то вполне можно избежать ненужных тестов.
ИМХО первый же пример DocumentDto vs Document
уже дает понять, что где-то что-то не так.
Если у вас действительно настолько похожие модели данных - стоит перестать следовать культу карго и просто сделать одну модель.
Если же модели данных сильно отличаются - автомаппер вам не поможет, вы словите больше геморроя, чем получите плюшек
Не согласен, разделять модели на Dto и Entity хороший подход, например частая ситуация, когда у вас у модели в базе есть поля для аудита, например created_at и created_by, так же is_deleted для soft delete. В таком случае Dto похож на Entity но часть полей в нём отстуствует и мапперы тут хорошо вписываются
Часто это показывает, что зависимости неправильно расставлены, и таким образом пытаются отстранится от инфраструктурных частей. От которых вообще не надо зависеть.
В данном примере наверное лучше сравнивать необходимость использования User (entity) и UserDomainModel.
В 99% случаев у них одинаковые свойства, которые приходится маппить и это создаёт дополнительную ненужную сложность, а с использованием автомапперов довольно часто к неожиданным ошибкам в рантайме.
В вашем случае Domain Model ещё и является анемичной, поэтому не ясна причина создания таких моделей.
Я догадываюсь, что вы создаёте анемичные доменные модели в Core/Domain слое, а сущности в Infrastructure/Persistense и поэтому вынуждены маппить модельки?
Люди придумывают себе проблемы из ниоткуда. Все ваши фреймворки для маппинга приводят к проблемам. Это неявно, это сложно расширять правилами. Сложно найти то место где это магически сделано, используя простой переход к реализации. Экономите пару минут, но потом тратите неделю на фикс. Можно просто использовать, например, IConvert<TFrom, TTo>. И внедрять куда надо. Это никогда не сломается при переименовании или апдейте фреймворка. И это можно мокать, если есть на то потребность в тесте. Если IConvert лень часто внедрять, и моментное удобство дороже быстрого перехода к реализации, то можно обернуть а сервайслокатор, внедрить его и вызывать для всех маппингов (что по мне не перевешивает удобство перехода к реализации).
Сложно найти то место где это магически сделано, используя простой переход к реализации.
Если библиотека использует кодогенерацию, то можно использовать простой переход к реализации.
Вы тогда ограничиваете себя правилами кодогенерацией фреймворка. Код который нельзя править самому, ну не абсурд ли? Вот возьмите обычную задачу. Допустим там модель с 20ю полями и еще пара вложенных типов. Это сделать используя любое IDE и мультикаретку - пара минут. И нет никаких волшебст и зависимостей. Все. Вот он код. Захотел переименовал, захотел прописал еще что-то там.
Реализовать IConvert чтобы он нормально работал с IQueryable?
Все еще не очень убедительно.
Мне всегда интересно, почему во всех примерах самые простые модели? Потому что как только что-то не стандартное из примера так ковыряться неделю
Вот в модель User можно было к примеру добавить поле с выбором пола и показать как это из базы в дто и дальше во вьюху.
Но нет, это надо в инструкции смотреть, а потом просто гуглить ))
Mapster можно ещё использовать для создания полноценных независимых копий объекта. Не сказать что часто этим пользуются, но для сильно вложенных объектов ссылочного типа - это неплохое готовое решение.
Обновили мы как-то на старом проекте либу, и после этого приложение очень избирательно умерло, потому что свойство на базовой сущности из этой либы стало иметь protected set, вместо public set, и из-за этого похожий на ваш маппер перестал ставить значения этому свойству... Разумеется, баг нашли на проде, осознали далеко не сразу и, разумеется, обновление той либы было необходимо. Сколько же геморра мы получили.
Другая история. Поставили мы какие-то либы для автоматизации всего и вся в нашем проекте, а потом очень долго расхлебывали тонкости, например, hangfire не мог запустить таски, потому что он не мог восстановить какие-то определенные серилизованные им же value object'ы, которые по факту ничем и не отличались от других. Раньше же у нас был максимально простой шедулер, который работал хотя бы стабильно, который можно было удобно перезапустить, в утром можно было добавить/удалить ему таску и тд и тп.
Я это все к тому, что автоматизация – особенно такая, в которой нет особой необходимости – создаёт больше проблем, чем пользы. Ну сделал ты задачу быстрее (и то далеко не всегда), молодец, но потом ещё несколько лет всей команде грозит расхлебывать проблемы из-за новой либы, которую самостоятельно и в разумные сроки ты не поправишь, если что.
Хватит маппить все руками, используй Mapster