Pull to refresh

Comments 21

Всё конечно здорово. Статья до последнего держит в напряжении и не раскрывает главную загадку - о какой собственно библиотеке речь?

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

Почему-то мне показалось это очевидным из названия, добавил ссылку.

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

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

Из бенчмарков что я смотрел mapperly самый производительный, и по скорости + аллокациям практически идентичен мапингу ручками. AutoMapper значительно уступал.

Все зависит от фич, у мапперов не большой выбор: либо собирать выражения и компилировать в рантайме (дает тормоза на первом использовании), либо использовать сурс-генераторы (производительность сразу на уровне). Конкретно mapperly не умеет проекции из IQueryable, только в следующем релизе добавят.

Я тоже не использую AutoMapper предпочитая явное неявному. Но пример автора, как мне кажется, неудачный, стоило упростить код, оставив только то, что относиться к заявленной теме.

Было бы достаточно следующего:

public class Projection<TSource, TResult>
{
    private readonly Lazy<Func<TSource, TResult>> _lazyDelegate;

    public Projection(Expression<Func<TSource, TResult>> expression)
    {
        Expression = expression;
        _lazyDelegate = new Lazy<Func<TSource, TResult>>(Expression.Compile, LazyThreadSafetyMode.PublicationOnly);
    }

    internal Expression<Func<TSource, TResult>> Expression { get; }

    internal Func<TSource, TResult> Delegate => _lazyDelegate.Value;

    public TResult Map(TSource source) => Delegate(source);
}

public static class ProjectionExtensions
{
    public static IQueryable<TDestination> Projection<TSource, TDestination>(
      this IQueryable<TSource> queryable, Projection<TSource, TDestination> projection)
    {
        return queryable.Select(projection.Expression);
    }

    public static IEnumerable<TDestination> Projection<TSource, TDestination>(
      this IEnumerable<TSource> enumerable, Projection<TSource, TDestination> projection)
    {
        return enumerable.Select(projection.Delegate);
    }
}

Полный код с рабочим примером:
https://gist.github.com/Zerg903/007967724a9d37f19856b083a1b6bf6e

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

    public static readonly Projection<SubCategory, SubCategoryDetails> SubCategoryDetails = new(x => new SubCategoryDetails
    {
        Id = x.Id,
        Active = x.Active,
        // тут вложенная проекция:
        Category = CategoryLookupDetails.ProjectTo(x.Category),
        Name = x.Name,
        Description = x.Description,
        CreatedDate = x.CreatedDate,
        ModificationDate = x.ModificationDate
    });

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

хм, проверил код без использования ProjectionSingleVisitor, как предложил @zerg903, и с вложенными проекциями маппится нормально. ЧЯДНТ?

EF Core может тихонько свалится в client side evaluation и даже как-то работать.

Я как-то забыл даже про DelegateDecompiler, тоже вариант, но как по мне более магический, потенциально может затруднить обновление. Надо попробовать.

Как-то упускается супер полезная фича у автомаппера как валидация конфигурации. В процессе активной разработки мне юнит тесты часто сыпят ошибки что новые поля не замаплены

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

Хотя бы иметь ассерт уже полезно, остальное руками доделать можно

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

Ручной маппер это полный контроль, что не может не радовать. Чем меньше магии, тем лучше. Но чем больше кода пишешь, тем чаще ловишь себя на мысли, что довольно много времени занимаешь тупым обезьяньим трудом. Трудом, который способна сделать за нас машина. Т.е. с одной стороны, мы автоматизируем работу людей с помощью разработки ПО. С другой стороны, как сапожник без сапог, жмём много кнопок там, где можно было бы их не жать.

Мы проверили как в старой рекламе на свеженьком небольшом проекте, just for fan. Примерно половину проекта писали на ручном маппинге. Вторую половину на Mapster-е в режиме "маппинг на пределе". Результаты. На второй половине проекта, разработка ведётся ощутимо быстрее. Меньше ошибок, ощутимо! Особенно при развитии модели данных. Больше времени посвящается интересным задачам, просто дышится легче.

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

А что означает "Mapster-е в режиме "маппинг на пределе""?

Sign up to leave a comment.

Articles