Pull to refresh

Вы подготовились к приходу AutoMapper?

.NET *

Введение

Данная статья предназначена к прочтению разработчикам и архитекторам распределенных систем на платформе .NET. В ней будет рассмотрен гибкий каркас для объектно-объектного преобразования (далее маппинга). Так же будут рассмотрены некоторые аспекты Domain-Driven Design’а.

Зачем мне нужен объектно-объектный маппинг?

Следуя основным принципам DDD, мы реализуем так называемую Rich Domain Model (эти объекты также должны соответствовать принципу POxO). Объекты реального мира, нашедшие отражение в нашем приложении частенько так же передают достаточную сложность, следовательно, достаточно корректно построенная модель крайне тяжело поддаётся перемещению между слоями приложения (не путать с легкостью вносимых изменений). Data Transfer Object Тем не менее достаточно часто появляется необходимость “распределения” (я имею ввиду создание промежуточных сущностей, а не растекание модели по слоям) модели между слоями, для отображения, к примеру, атрибутов её сущностей пользователям (в шаблонах представления MVx), а так же передаче по сервисам (Data Transfer Object). Порой бывает даже, что модель “распределяется” для тестирования некоторых аспектов. Предположим, мы в Африке, у нас банановая плантация, всё классно, выращиваем, продаём, выращиваем, продаём, но тут внезапно внутренний рынок переполняется и нам надо расширятся (к примеру вести бананы в Россию), мы пишем WCF сервис, который будет слать наши бананы. Так как бананы в Африке имеют несколько иное значение, чем в России, то, соответственно нам понадобятся лишь некоторые атрибуты (остальные фактически не имеют значения), которые мы забубеним в наш DTO
Правильнее было бы дать классу BananaWrapper название BananaDTO, для того, чтобы точно отображать его функциональное назначение, но я оставлю название таким для большего уровня абстракции, к примеру, если нам понадобится сделать автомат по продажи бананов и поместить этот объект в Presenter Model
Хочу заметить, что порой задача преобразования объектов становится довольно-таки нетривиальной и в лучшем случаем выглядит примерно подобным образом (это решение в лоб, есть ещё более изощренные методы ;)):
  1. public class Banana
  2. {
  3.  public string Country { get; set; }
  4.  public double Price { get; set; }
  5.  public double NationalTax { get; set; }
  6.  public bool CocaineInjection { get; set; }
  7.  public bool FreshForFooding { get; set; }
  8.  public string AkunamatataName { get; set; }
  9.  public string BananaGeneration { get; set; }
  10.  public int Age { get; set; }
  11. }
  12.  
  13. public class BananaWrapper
  14. {
  15.  public string Country { get; set; }
  16.  public double Price { get; set; }
  17.  public int Age { get; set; }
  18. }
  19. public class BananaMapper
  20. {
  21.  public BananaWrapper GetWrapper(Banana banana)
  22.  {
  23.   return new BananaWrapper
  24.     {
  25.      Country = banana.Country,
  26.      Price = banana.Price,
  27.      Age = banana.Age
  28.     };
  29.  }
  30. }
* This source code was highlighted with Source Code Highlighter.
Думаю, что такой код писать, а тем более сопровождать, мало кому будет в радость, в последнее время я как раз частенько встречался с такого рода задачами, и находился в поиске решения проблемы.

AutoMapper

И тут на сцену выходит наш персонаж – AutoMapper, и сразу же говорит мне: — послушай, ты что такое пишешь? тебе не лень? ты не боишься допустить ошибок? хочешь я тебе помогу?!.. Я конечно же соглашаюсь, и получаю в ответ следующее решение моей проблемы:
  1.  public class BananaMapper
  2. {
  3.   public BananaMapper()
  4.   {
  5.    Mapper.CreateMap<Banana, BananaWrapper>();
  6.   }
  7.  
  8.   public BananaWrapper GetWrapper(Banana banana)
  9.   {
  10.    return Mapper.Map<Banana, BananaWrapper>(banana); ;
  11.   }
  12. }
* This source code was highlighted with Source Code Highlighter.
Класс, и это действительно всё, что мне понадобится. Сложность, вышележащего примера снизилась в моих глазах до нуля. Итак, что же за механизмы лежат внутри AutoMapper? AutoMapper проверяет есть соответствующие поля в указанных типах, соответствие проводится как по имени свойства, так и по его типу. Даже такие нюансы, как Product.Name и ProductName будут учтены и обработаны автоматически (wow!). Плюс ко всему методы GetXXX() будут ложится на свойства XXX (да, ну и естественно для особо раздражительных все эти прелести можно отключить и переопределить всё в своих собственных таблицах соответствия (далее мапках)). Кастомная конфигурация выглядит примерно следующим образом:
  1. Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
  2.   .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
  3.   .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))
  4.   .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));
* This source code was highlighted with Source Code Highlighter.
Кстати, все ваши кастомные конфигурации легко поддаются проверке с помощью следующего метода:
  1. Mapper.AssertConfigurationIsValid();
* This source code was highlighted with Source Code Highlighter.
Так же не плохо работает с:

История

Проект появился в конце’08-начале’09, около полугода находился в версии 0.31, сейчас же добрался до RC 1.0, думаю, что релиз уже совсем скоро.

Overhead?

banana Дебаты по поводу того, насколько быстрее будет работать AutoMapper и присвоение свойств в ручную (и прочие мульки) я игнорирую, т.к. готов пойти на любые жертвы производительности, если получу ясный, читабельный код. Ах, да, автор AutoMapper позаботился об этих вопросах и написал бенчмарки, смотреть здесь: http://code.google.com/p/automapperhome/source/browse/#svn/trunk/src/Benchmark

Ресурсы

Скачать проект, а так же ознакомиться с исходными кодами можно здесь: http://code.google.com/p/automapperhome/ Обсуждения каркаса здесь: http://groups.google.com/group/automapper-users Так же примеры использования есть здесь: http://automapper.codeplex.com/ Кстати проект разрабатывает Joe Benninghoven, который так же пишет BDD фреймворк для .NET под названием NBehave.
Tags:
Hubs:
Total votes 34: ↑23 and ↓11 +12
Views 37K
Comments 37
Comments Comments 37

Posts