Как стать автором
Обновить
31
0
Кирилл @teoadal

Senior .NET Developer

Отправить сообщение
Да, я думал об этом и даже набросал небольшие тесты в отдельной ветке. Тот, который System.Text.Json.JsonSerializer работает очень хорошо. Максимальную производительность, судя по документации, должна обеспечивать работа с Utf8JsonReader — но до её тестирования пока руки не дошли.

Тестирование производилось на следующей конфигурации (так как мы имеем дело с preview, то привожу её полностью):
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17134.81 (1803/April2018Update/Redstone4)
Intel Core i5-7400 CPU 3.00GHz (Kaby Lake), 1 CPU, 4 logical and 4 physical cores
.NET Core SDK=3.0.100-preview8-013656

[Host]: .NET Core 3.0.0-preview8-28405-07 (CoreCLR 4.700.19.37902, CoreFX 4.700.19.40503), 64bit RyuJIT
Core: .NET Core 3.0.0-preview8-28405-07 (CoreCLR 4.700.19.37902, CoreFX 4.700.19.40503), 64bit RyuJIT

Job=Core Runtime=Core

Десериализация (.NET Core 3.0 preview):
Newtonsoft: 1 (allocation 34.87 MB)
VNext: 0.76 (allocation 9.3 MB)
Velo: 0.39 (allocation 11.37 MB).

Сериализация:
Newtonsoft: 1 (allocation 24.85 MB)
VNext: 0.82 (allocation 16.59 MB)
Velo: 0.37 (allocation 5.91 MB).

Думаю, что можно уже готовиться переходить на System.Text.Json.JsonSerializer. Из коробки он работает значительно лучше Newtonsoft: аллокация при десериализации очень низкая — как минимум GC будет благодарен.
Хм. Конечно, было бы здорово сделать бенчмарк и дать ссылку на него код, но если вам не удобно, то хоть примерный код набросайте — как вы это делаете?

Ещё у меня есть проблема с примерами. Я вот бегло посмотрел «Using the code» на странице проекта, и там написано:

var newobj = fastJSON.JSON.Instance.ToObject(jsonText);

Но в static-классе JSON нет свойства Instance. Возможно, у вас какой-то другой код? Может быть, вы дадите ссылку на nuget? Как я уже писал, я взял вот отсюда (версия 2.2.4). Если перейти с nuget на страницу проекта — то это именно та, которую вы дали.
Я установил nuget-пакет отсюда и обновил код бенчмарков: deserialization и serialization. Так как я не знаю обычных способов использования fastJSON, то я пошёл по пути наименьшего сопротивления: в библиотеке есть статический класс JSON с двумя методами — ToObject и ToJson. Я использовал именно их. Возможно, вы подскажете какие-то более оптимальные способы.

Результаты тестирования для десериализации (таблицу вставить почему-то не получилось):
Newtonsoft: 1.00 (аллокация 35.47 MB)
FastJson: 0.97 (аллокация 48.81 MB)
JDeserializer: 0.42 (аллокация 12.36 MB)

Результаты тестирования для сериализации:
Newtonsoft: 1.00 (аллокация 25.44 MB)
FastJson: 1.21 (аллокация 54.8 MB)
JSerializer: 0.38 (аллокация 5.93 MB)
Я сделал так, чтобы было больше похоже на то, что делалось с помощью Reflection (PropertyInfo.GetValue/SetValue). Текст больше для тех, кто не очень разбирается в ExpressionTrees, поэтому хотелось, чтобы переход был плавный.

Также, у меня немного другая ситуация: аргумент функции типа object, а не TEntity. То есть мне в самом начале нужно сделать приведение типа и сохранить результат в переменную, а значит без тела (Expression.Body) как мне кажется не получится.

При этом вы правы, чтобы сократить количество действий можно было формировать набор Expression.Bind и в самом конце сделать Expression.MemberInit. Т.е. скомпилированный код будет вида:
x => 
{ 
     var source = (TIn) x; 
     return new TOut 
     {
         Property = source.Property;
         ...
     } 
}

А код построения функции:
private Func<object, TOut> BuildConverter(Type sourceType)
{
    var parameter = Expression.Parameter(typeof(object), "x");
    var source = Expression.Variable(sourceType, "source");

    var bindings = sourceType.GetProperties()
        .Where(p => _outProperties.ContainsKey(p.Name))
        .Select(p => Expression.Bind(_outProperties[p.Name], Expression.Property(source, p)));

    var body = Expression.Block(new[] {source}, 
        Expression.Assign(source, Expression.Convert(parameter, sourceType)),
        Expression.MemberInit(Expression.New(_outConstructor), bindings));
    
    return Expression.Lambda<Func<object, TOut>>(body, parameter).Compile();
}

А зачем? Этот велосипед, как я писал, нужен для демонстрации работы с ExpressionTrees. AutoMapper значительно мощнее. Если у меня когда-нибудь (по объективным замерам) возникнут проблемы с производительностью там, где происходит маппинг — возможно я вернусь к этому велосипеду.

FastExpressionCompiler посмотрю, спасибо. Есть у меня мысль написать про то, как я парсил JS и компилировал его с помощью ExpressionTrees — думаю там это может пригодиться.
Спасибо большое, интересная статья.

Однако, в комментариях правильно заметили: «суть автомаппера не банальное перекладывание из одного в другое, в общем-то проекции, возможность инъекции...».

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

Мне кажется, что это сложная задача для кодогенерации. Впрочем, я в ней не специалист.

Да, я понял, что вы поддерживаете использование AutoMapper :)

Да, всё верно. Кодогенерация — тоже способ решения подобных задач. Впрочем, у этого есть и минусы: захламление кодовой базы и необходимость её поддержки, так как модели могут (и будут) меняться.

Кстати, нет ли у вас под рукой хорошей статьи по использованию Roslyn для кодогенерации? Чтоб, так сказать, «на пальцах». Давно хотел освоить, но видел только какие-то сложные примеры.
Статья интересная.

При этом представляется, что в подобном случае лучше разбить шаг «десериализация-связывание» на два. Т.е. мы на первом шаге просто десериализуем поступившие данные из DTO и лишь затем создаем бизнес-объекты. В этом случае links будет содержать id вершин, а вершины будут поступать в отдельном словаре вершин из которых мы будем доставать их по id.

Да, это чуть сложнее и требует работы как на стороне сервера (где мы преобразуем бизнес-объекты в DTO), так и на стороне клиента. Но выглядеть будет точно проще.

Информация

В рейтинге
Не участвует
Откуда
Нижний Новгород, Нижегородская обл., Россия
Зарегистрирован
Активность

Специализация

Бэкенд разработчик, Фулстек разработчик
Ведущий
SQL
C#
ASP.NET MVC
Linq
.NET
ASP.NET
PostgreSQL