Comments 18
А среди аналогов AutoMapper точно не нашлось ничего подходящего?
Даже беглый поиск по гитхабу выдает кучу либ:
- ExpressMapper
- Mapster
- не к ночи будь помянут EmitMapper
- и даже в compile time: DesignTimeMapper
configuration.CreateMap<Person, PersonViewModel>()
.ForMember(vm => vm.FullName,
o => o.MapFrom(p => $"{p.FirstName} {p.LastName}"))
.ForMember(vm => vm.Age,
o => o.MapFrom(p => Helpers.CalculateAge(p.BirthDate)))
.ForMember(vm => vm.Foo, o => o.Ignore())
.ForMember(vm => vm.Bar, o => o.UseValue(5))
mappingConfiguration.CreateMapping<Person, PersonViewModel>(
p => new PersonViewModel
{
FullName = $"{p.FirstName} {p.LastName}",
Age = Helpers.CalculateAge(p.BirthDate),
Bar = 5,
Foo = MappingOptions.Ignore()
});
Данный код не только более компактный, но и более дружественен к интелисенсу.
configuration.CreateMap<Person, PersonViewModel>()
.ForMember(vm => vm.FullName,
o => o.MapFrom(p => $"{p.FirstName} {p.LastName}"))
.ForMember(vm => vm.Age,
o => o.MapFrom(p => Helpers.CalculateAge(p.BirthDate)))
.ForMember(vm => vm.Foo, o => o.Ignore())
.ForMember(vm => vm.Bar, o => o.UseValue(5))
// вызываем собственное расширение, которое расширяет конфигурацию
.ForEntityToDtoCommon(); // собственное расширение
// плюс ещё какие-нибудь...
Ну и MapFrom это отражение, которое может транслироваться в SQL, а ваш new PersonViewModel — не может, и сделать это будет крайне затруднительно. Хотя, если получится, то будет даже круто. Но не забывайте про необходимость до-определения, это нужно!
FlashMapper использует только Expression<Func> из-за чего невозможно для преобразования вызывать другие методы со сложной логикой. :(
И еще есть такой замечательный проект для микробенчмарков: BenchmarkDotNet. Де-факто, стандарт для измерения производительности.
Довольно бегло пролистал вашу статью, но не нашел толком информации по вложенным маппингам, а так же поддержки выражений для селекта из DB-провайдеров.
В автомаппере чаще всего использую именно что то в роде
db.Users.GetAll<User>().ProjectTo<UserViewModel>()
и автомаппер автоматом подставляет нужное ваыражение, чтобы провайдер забрал только нужные поля из базы.
Аналога ProjectTo тоже нет, но согласен, что функция это очень интересная, постараюсь ее реализовать в будущем.
Чтобы можно было что-то наподобие «если у родителя есть свойство типа `List`, где T — наследник ModelBase, то при отображении его свойств тип Guid может маппиться на T таким-то ресолвером».
Маппинг по конвенциям, звучит как задача для EmitMapper
НО,
Во-первых, можете полить меня кипятком, но для сложных случаях — где сокращение синтаксиса от обычного создания нового экземпляра и перенос свойств вручную?
Во-вторых, очень уж я боюсь судьбы фронтенда для .NET, что начнет появляться громадное количество микро-фреймворков просто потому что «хотелось чуть перламутровее».
В-третьих, сама по себе идея автомаппинга сильно различающихся сущностей мне кажется порочной.
П.С. Извиняюсь дико, но не «Dependancy» а «Dependency».
mappingConfiguration.CreateMapping<Person, PersonViewModel>(
p => new PersonViewModel
{
FullName = $"{p.FirstName} {p.LastName}",
Age = Helpers.CalculateAge(p.BirthDate),
Bar = 5,
Foo = MappingOptions.Ignore()
});
Этот код только выглядит как создание нового экземпляра и перенос свойств вручную. На деле это лямбда-выражение, из которого компилятор c# генерит ExpressionTree, из которого в свою очередь можно получить всю нужную информацию для создания маппинга. В примере сопоставляются только различающиеся свойства. Идентичные свойства из обоих классов сопоставятся автоматически.
Да уж, жестко я опечатался, прямо в названии проекта, сборки и пакета.
В автомаппере мне никогда не нравился его монструозный синтаксис. Просто, чтобы связать два свойства в разных классах нужно прописать три лямбды в конфигурации, чтобы проигнорировать свойство — две.
А что, написать расширение к AutoMapper, «включающий» необходимый и удобный вам синтаксис определений отражения — не? Он же расширяемый вдоль и поперёк. Можно даже авторегистрацию маппинга приделать на атрибутах.
Кроме того, без проекций (ProjectTo) любой маппер является бесполезной игрушкой на выброс. Только с проекциями можно избежать проблемы SELECT N+1, за что так любят ругать ORM-ы. Если нет проекций, такой маппер бесполезен.
Самая большая проблема – перестает работать «find usages». И ты начинаешь мучительно искать, кто же и где пишет в это конкретное поле.
А еще часто появляются либо зубодробительные конфигурации кастомных маппингов (с чем автор и борется в статье). Либо методы, в которых заполняются поля объекта, которые не смаппились по-умолчанию.
Все это очень запутывает =(
mappingConfiguration.Convert(model).To<UserDb>();
можно сократить до mappingConfiguration.ConvertTo<UserDb>(model);
Хотя это можно сделать и расширением класса.
FlashMapper — альтернатива автомапперу