ASP.NET MVC 4 в действии
Джеффри Палермо
Жизнь до AutoMapper
Перед тем, как начать использовать AutoMapper, давайте создадим функционал без него. Вероятно мы заметим некоторые сложности, которые сможет разрешить AutoMapper. Поиск повторяющегося кода, логика в представлениях и скучная работа с глубокими иерархиями объектов – основные моменты, которые мы хотим разгрузить.
Представьте себе представление, которое демонстрирует информацию о клиенте. В главе 2 мы обсуждали некоторые тривиальные приложения, в которых можно использовать постоянные объекты доменной модели в качестве источника данных. Следующий листинг демонстрирует такой сценарий.
Листинг 11-1: Доменная модель
@model Core.Model.Customer
<h2>Customer: @(Model.Name.First + " " + Model.Name.Middle + " " + Model.Name.Last) </h2>
<div class="customerdetails">
<p>Status: @Model.Status </p>
<p>
Total Amount Paid: $
@Model.GetTotalAmountPaid()
</p>
<p>
Address: @Model.ShippingAddress.Line1,
@Model.ShippingAddress.Line2,
@Model.ShippingAddress.City,
@Model.ShippingAddress.State.DisplayName
@Model.ShippingAddress.Zip
</p>
</div>
Строка 3: Форматирует сложные компоненты
Строки 7-8: Применяет стандартное форматирование
Строки 13-14: Глубоко рассматривает доменные объекты
Эта разметка чересчур сложна для простого содержимого, которое она демонстрирует. Она включает в себя общие правила форматирования, такие как применение знака доллара в значениях decimal
и какое-то подозрительное форматирование имен, которое явно будет выглядеть неправильно, если будет отсутствовать middle name
.
При отображении страницы существует не только опасность того, что она будет выглядеть неправильно, но и того, что она не будет отображаться вообще. Что если ShippingAddress
содержит null
? Мы увидим неприятное исключение при обращении к null
на желтом экране смерти, который сопровождает основные ошибки ASP.NET. Все эти проблемы появляются потому, что представления непосредственно зависят от доменной модели - и пользовательский интерфейс знает слишком много о базовой логике программы.
Из примеров главы 2 и предыдущего раздела мы знаем, что в большинстве случаев лучше разработать пользовательскую модель специально для представления. Преобразование из доменной модели, или ее проекция в презентационную модель – прямая задача программирования. Возьмите значения из объекта-источника и скопируйте их в правильное место целевого объекта. Добавьте немного форматирующего и выравнивающего кода, и проекция готова. Такую логику можно легко протестировать.
Вот пример самодельного маппера.
Листинг 11-2: Преобразование объектов вручную
public class CustomerInfoMapper
{
public CustomerInfo MapFrom(Customer customer)
{
return new CustomerInfo
{
Id = customer.Id,
Name = new NameFormatter().Format(customer.Name),
ShippingAddress = new AddressFormatter().Format(customer.ShippingAddress),
Status = customer.Status ?? string.Empty,
TotalAmountPaid = customer.GetTotalAmountPaid().ToString("c")
};
}
}
Строка 3: Принимает исходный тип, возвращает нужный
Строки 7-11: Выполняет маппинг вручную
Класс в листинге 11-2 можно протестировать, и он отделяет представление от сложности доменной модели. С его помощью представление получает данные в той форме, в которой они должны быть выведены.
Вот наше представление, обновленное для работы с CustomerInfo
вместо Customer
.
<h2>Customer: @Model.Name</h2>
<div class="customerdetails">
<p>Status: @Model.Status</p>
<p>Total Amount Paid: @Model.TotalAmountPaid</p>
<p>Address: @Model.ShippingAddress</p>
</div>
Так гораздо лучше. Предыдущая разметка содержит больше что и где, и меньше указаний как.
Хотя сценарий ручного преобразования из листинга 11-2 уже гораздо лучше рендеринга доменной модели напрямую, его все еще утомительно писать, дорого поддерживать, он хрупкий и подвержен ошибкам. Мы можем его протестировать, но в системе с десятками экранов такое тестирование может застопорить проект.
Теперь мы понимаем, какие проблемы решает AutoMapper, и можем начать использовать его для решения задач преобразования. AutoMapper позволяет отказаться от кода ручного преобразования и дает нам возможность включить общие или специальные пользовательские правила форматирования. Вместо обязательного кода, который мы писали в листинге 11-2, мы можем объявить преобразование, и AutoMapper представит для нас соответствующее поведение.
Декларативное vs. императивное программирование
Императивное программирование подразумевает традиционный код, который мы обычно пишем. Он выражает действия в виде серии строк кода, указывающих логическую последовательность и назначение. Императивный код состоит из сложных алгоритмов и логических утверждений, которые указывают точную последовательность операций.
С другой стороны, декларативное программирование указывает, что должно быть сделано, а не как это сделать. Декларативных код прост - это просто утверждение, а не набор инструкций.
Канонический пример декларативного программирования - это регулярные выражения. Представьте воспроизведения текстового поиска, представленное сложным регулярным выражением с императивными операторами
if
и циклами. Избежать этого бремени (и найти эффективные инструменты) - путь к быстрому созданию и беспрепятственной поддержке.
Вот пример объявления конфигурации AutoMapper:
CreateMap<Customer, CustomerInfo>()
.ForMember(x => x.ShippingAddress, opt =>
{
opt.AddFormatter<AddressFormatter>();
opt.SkipFormatter<HtmlEncoderFormatter>();
});
Мы вернемся к этому коду и разберем код конфигурации AutoMapper далее в этой главе.