Главная страница   /   11.1. Жизнь до AutoMapper (ASP.NET MVC 4 в действии

ASP.NET MVC 4 в действии

ASP.NET MVC 4 в действии

Джеффри Палермо

11.1. Жизнь до 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 далее в этой главе.