Представление модели через пользовательский интерфейс
Теперь, когда доменная модель и инфраструктура NHibernate заданы и функционируют, мы можем снова перенести наше внимание к ASP.NET MVC проекту. Мы оставили проект близким к используемому по умолчанию шаблону проекта с тем, чтобы сохранить его простоту, а также явно идентифицировать дополнительные компоненты, необходимые для сохранения каждого посетителя сайта. Рисунок 15-8 демонстрирует структуру UI-проекта.
Рисунок 15-8: Дополнительные компоненты проекта выделены цветом. Мы добавили несколько файлов для фиксации и отображения посетителей.

Как вы помните (из рисунка 15-1), в верхней части каждой страницы сайта демонстрируются недавние посетители сайта. Для того чтобы распределить это представление между всеми страницами, мы подключили частичное представление к мастер-странице, Site.Master
. Мы рассматривали эту возможность в главе 3, поэтому не будем снова рассматривать ее подробно.
На самом верхнем уровне мы добавили атрибут фильтра действий к каждому контроллеру. Если сайт содержит множество контроллеров, мы предполагаем, что необходимо ввести пользовательский ControllerActionInvoker
для всех контроллеров и добавить фильтр для всех контроллеров. В этом примере проект содержит только HomeController
, который продемонстрирован в следующем листинге. Обратите внимание на фильтры действий, применяемые на уровне класса.
Листинг 15-11: Фильтры действий, применяемые к контроллерам для поддержания разделенности понятий
using System.Web.Mvc;
namespace UI.Controllers
{
[HandleError]
[VisitorAdditionFilter(Order = 0)]
[VisitorRetrievalFilter(Order = 1)]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
}
}
Строка 5: Применяет
VisitorAdditionFilter
Строка 6: Применяет
VisitorRetrievalFilter
Мы ввели два фильтра, VisitorAdditionFilter
и VisitorRetrievalFilter
. Мы применили необязательный параметр Order
для того, чтобы убедиться, что они выполняются в запланированном порядке. Порядок, в котором атрибуты применяются к классу, не является гарантированным порядком выполнения.
Мы хотели бы сохранять нового посетителя, а затем извлекать список недавних посетителей и передавать их в представление. Следующий листинг демонстрирует оба фильтра действий.
Листинг 15-12: Взаимодействие фильтров действий с доменной моделью
using System.Web.Mvc;
using Core;
namespace UI
{
public class VisitorAdditionFilter : ActionFilterAttribute
{
private readonly IVisitorRepository _repository;
public VisitorAdditionFilter(IVisitorRepository repository)
{
_repository = repository;
}
public VisitorAdditionFilter()
: this(new VisitorRepositoryFactory().BuildRepository())
{
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var builder = new VisitorBuilder();
Visitor visitor = builder.BuildVisitor();
_repository.Save(visitor);
}
}
public class VisitorRetrievalFilter : ActionFilterAttribute
{
private readonly IVisitorRepository _repository;
public VisitorRetrievalFilter(IVisitorRepository repository)
{
_repository = repository;
}
public VisitorRetrievalFilter()
: this(new VisitorRepositoryFactory().BuildRepository())
{
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Visitor[] visitors = _repository.GetRecentVisitors(10);
filterContext.Controller.ViewData[Constants.ViewData.VISITORS]= visitors;
}
}
}
Строка 15, 36: Создает репозиторий с помощью фабрики
Строка 19, 40: Выполняет работу в
OnResultExecuting
Строка 22-23: Сохраняет новый
Visitor
Строка 42-43: Хранит недавних посетителей во
ViewData
Каждый фильтр довольно прост. Большая часть кода нужна всего лишь для управления зависимостью от IVisitorRepository
и создания репозитория из фабрики. Три строки, которые представляют интерес, находятся в методе OnResultExecuting
. Мы создаем посетителя и сохраняем его. Затем мы получаем недавних посетителей и помещаем их во ViewData
. Класс VisitorBuilder
не продемонстрирован, но он довольно прост, конструирует Visitor
и наполняет его информацией из HttpRequest
.
Следующий представляющий интерес файл – это частичное представление Visitors.cshtml
, расположенное в /Views/Shared/Visitors.cshtml
.
Листинг 15-13: Отображает недавних посетителей
@model Core.Visitor[]
<div style="text-align: left">
<h3>Recent Visitors</h3>
@foreach (var visitor in ViewData.Model)
{
@visitor.VisitDate @:-
@visitor.IpAddress @:-
@visitor.LoginName @:-
@visitor.PathAndQuerystring <br />
@visitor.Browser <hr />
}
</div>
Это частичное представление добавляется на страницу через мастер-страницу. Ожидается, что список посетителей будет располагаться в ViewData.Model
с тем, чтобы список мог бы отображаться способом по умолчанию. В верхней части мастер-страницы следующий код просто передает список посетителей в частичное представление:
<div id="footer">
@{
var partialName = Constants.Partials.VISITORS;
var viewData = ViewData[Constants.ViewData.VISITORS];
}
@Html.Partial(partialName, viewData)
</div>
Мы используем константы для того, чтобы представления не содержали продублированного строкового текста. Поскольку информация о входе в систему и отображении посетителей является сквозной в рамках всего приложения, мы предприняли шаги для того, чтобы факторизировать логику с тем, чтобы она могла совместно использоваться всеми контроллерами приложения.
Давайте повторим то, что мы проделали:
- Поместили логику сохранения за рамки интерфейса, который не принадлежит к UI-проекту
- Использовали фильтры действий с тем, чтобы ни один единичный контроллер не был ответственен за знание того, как выполнять интеграцию с
IVisitorRepository
- Создали частичное представление для того, чтобы иметь макет недавних посетителей
- Передали полномочия частичному представлению мастер-страницы с тем, чтобы индивидуальным представлениям не приходилось заботиться об отображении информации о посетителе
Теперь все находится на своих местах и может быть объединено в единое целое.