Comments 16
UFO just landed and posted this here
Да, всё верно. Кодогенерация — тоже способ решения подобных задач. Впрочем, у этого есть и минусы: захламление кодовой базы и необходимость её поддержки, так как модели могут (и будут) меняться.
Кстати, нет ли у вас под рукой хорошей статьи по использованию Roslyn для кодогенерации? Чтоб, так сказать, «на пальцах». Давно хотел освоить, но видел только какие-то сложные примеры.
Кстати, нет ли у вас под рукой хорошей статьи по использованию Roslyn для кодогенерации? Чтоб, так сказать, «на пальцах». Давно хотел освоить, но видел только какие-то сложные примеры.
0
UFO just landed and posted this here
Спасибо большое, интересная статья.
Однако, в комментариях правильно заметили: «суть автомаппера не банальное перекладывание из одного в другое, в общем-то проекции, возможность инъекции...».
В своей статье я дал лишь самый простой пример — копирование свойства в свойство. Проекции я не создавал, типы не изменял, данные в конструктор не пробрасывал и т.п. Как вы думаете, с помощью кодогенерации возможно будет задать какие-то дополнительные правила преобразования из одного типа в другое? Или организовать взаимосвязь мапперов, когда в одном объекте есть другой, на который тоже существует схема маппинга?
Мне кажется, что это сложная задача для кодогенерации. Впрочем, я в ней не специалист.
Да, я понял, что вы поддерживаете использование AutoMapper :)
Однако, в комментариях правильно заметили: «суть автомаппера не банальное перекладывание из одного в другое, в общем-то проекции, возможность инъекции...».
В своей статье я дал лишь самый простой пример — копирование свойства в свойство. Проекции я не создавал, типы не изменял, данные в конструктор не пробрасывал и т.п. Как вы думаете, с помощью кодогенерации возможно будет задать какие-то дополнительные правила преобразования из одного типа в другое? Или организовать взаимосвязь мапперов, когда в одном объекте есть другой, на который тоже существует схема маппинга?
Мне кажется, что это сложная задача для кодогенерации. Впрочем, я в ней не специалист.
Да, я понял, что вы поддерживаете использование AutoMapper :)
0
А в автомаппере, поле удалилось, ну и хрен с ним, тихонько игнорируем.
configuration.AssertConfigurationIsValid(); позволяет не игнорировать тихонько, а получить явный exception при старте приложения.
0
Ну что же. Добавьте в свой велосипед еще скорости FastExpressionCompiler .
И все-таки странно что Automapper слил, есть им еще куда стремиться, хотя думал по той тропинке столько зверей потопталось, что дальше уже некуда.
И все-таки странно что Automapper слил, есть им еще куда стремиться, хотя думал по той тропинке столько зверей потопталось, что дальше уже некуда.
0
Автомапер, как мне кажется, сложнее внутри, а так идея одна и та же. Следущий шаг — генерация байткода, но это потребует куда больших усилий.
0
А зачем? Этот велосипед, как я писал, нужен для демонстрации работы с ExpressionTrees. AutoMapper значительно мощнее. Если у меня когда-нибудь (по объективным замерам) возникнут проблемы с производительностью там, где происходит маппинг — возможно я вернусь к этому велосипеду.
FastExpressionCompiler посмотрю, спасибо. Есть у меня мысль написать про то, как я парсил JS и компилировал его с помощью ExpressionTrees — думаю там это может пригодиться.
FastExpressionCompiler посмотрю, спасибо. Есть у меня мысль написать про то, как я парсил JS и компилировал его с помощью ExpressionTrees — думаю там это может пригодиться.
0
teoadal почему вы решили строить выражение как функцию с отдельным созданием экземпляра и последовательным присвоений значений из сущности в ДТО, а не построение просто лямбда-выражения? Вы ведь обычно пишите запрос через ORM как .Select(x => new T { Field = x.Field }), а не .Select(x => { var y = new T(); y.Field = x.Field; }). Мне кажется такое вариант куда ближе к ОРМ-ным и проще им парсится
То же делали маппер на проекте, но через Expression.MemberInit + набор Expression.Bind
То же делали маппер на проекте, но через Expression.MemberInit + набор Expression.Bind
public Expression<Func<TEntity, TDto>> GetMapExpr<TEntity, TDto>()
{
var entityType = typeof(TEntity);
var entityParam = Expression.Parameter(entityType, "x");
var entityProps = entityType.GetProperties();
var dtoType = typeof(TDto);
var dtoProps = dtoType.GetProperties();
var memberExpressions = ... сличение dtoProps и entityProps и построение Expresion.Bind ...
var newDTO = Expression.MemberInit(Expression.New(dtoType), memberExpressions);
var selector = (Expression<Func<TEntity, TDto>>)Expression.Lambda(newDTO, entityParam);
return selector;
}
0
Я сделал так, чтобы было больше похоже на то, что делалось с помощью Reflection (PropertyInfo.GetValue/SetValue). Текст больше для тех, кто не очень разбирается в ExpressionTrees, поэтому хотелось, чтобы переход был плавный.
Также, у меня немного другая ситуация: аргумент функции типа object, а не TEntity. То есть мне в самом начале нужно сделать приведение типа и сохранить результат в переменную, а значит без тела (Expression.Body) как мне кажется не получится.
При этом вы правы, чтобы сократить количество действий можно было формировать набор Expression.Bind и в самом конце сделать Expression.MemberInit. Т.е. скомпилированный код будет вида:
А код построения функции:
Также, у меня немного другая ситуация: аргумент функции типа 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();
}
0
Попробуйте FastExpressionCompiler, и не потому что компиляция быстрее (может для вас время старта не важно), а по этому что скомпилированный делегат может быть быстрее. Иногда, значительно.
0
Эх, хорошая штука эти ExpressionTrees. Я, правда, маппер не писал, но атрибутные валидацию данных и правовые фильтры, а также кастомные фильтры/сортировки для IQueriable прикручивал.
0
Да, мне тоже очень нравится, но только для простых задач. Например, парсить и создавать в функции из JavaScript — ад. Там возникает сразу целая куча проблем вроде того, что ExpressionTrees иногда сложно составлять «по ходу» кода. Иногда нужно заглянуть на пару шагов вперед, чтобы выражение получилось правильное.
0
Sign up to leave a comment.
Свой mapper или немного про ExpressionTrees