Portable Areas как вариант модульности в MVC

Один из первых вопросов, которым я задался после знакомства с азами технологии MVC3, это способ выделения и повторного использования функционала в нескольких веб-проектах.
В WPF или WinForms все просто и понятно — обособленный функционал изолируется в модуль, модуль компилируется в библиотеку, библиотека — подключается к проекту и повторно используется. Нужно лишь грамотно изолировать модули, и всё будет хорошо.

В MVC всё не так просто, если «обособленный функционал» — это набор контроллеров и вьюшек, реализующих, допустим, гостевую книгу, то просто так вынести их в отдельный модуль нельзя — MVC просто не найдет ваши контроллеры в соседних библиотеках.

Однако решение, конечно, есть, и его нам предлагает небезызвестная библиотека MvcContrib — open-source проект, неаффилированный Майкрософтом.

Изоляция нескольких взаимосвязанных контроллеров и вьюшек возможна в MVC3 и «из коробки» — для этого существуют так называемые «области» — areas. Однако вынести такие области в отдельный проект, на данный момент, без помощи MvcContrib нельзя.

Допустим, что у вас уже есть на сайте гостевая книга и есть желание повторно использовать её в другом проекте. Что надо сделать?

  1. Создать проект типа ClassLibrary и добавить в него референсы на System.Web.Mvc.dll, System.Web.Razor.dll (если вьюшки используют Razor), MvcContrib.dll и Microsoft.Web.Mvc.dll (последняя — для поддержки строгой типизации, можно пропустить).
    Зависимости portable area
  2. Создать структуру проекта, очень похожую на обычный MVC-шаблон. Единственное отличие, что папки Views, Controllers, etc. должны расположиться в корневой папке с именем нашей выделяемой области (portable area). В примере это — Guestbook. В данную структуру скопировать вьюшки и контроллеры, которые хочется обособить.

    Всем вьюшки в области необходимо выставить метод компиляции — embedded resource!
  3. Создать регистрационный класс, который и позволит связать основное MVC-приложение и выделенную область. Класс этот должен наследоваться от PortableAreaRegistration. В простейшем случае он может выглядеть так:

     public class GuestbookRegistration : PortableAreaRegistration
        {
            public override string AreaName
            {
                get { return "Guestbook"; }
            }
        }
    

    Единственный необходимый параметр — это поле AreaName с названием области.
    Однако, здесь же можно и изменить маршруты для выделенной области:

     public class GuestbookRegistration : PortableAreaRegistration
        {
            public override string AreaName
            {
                get { return "Guestbook"; }
            }
    
            public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)
            {
                base.RegisterArea(context, bus);
    
                context.MapRoute(
                   "Guestbook_Default",
                   "GuestbookPro/{action}/{id}",
                   new { controller = "Guestbook", action = "Index", id = UrlParameter.Optional });
    
            }
        }
    

    Дефолтный маршрут выглядит, как нетрудно догадаться, так: AreaName + «/{controller}/{action}/{id}». То есть полный путь до индексового экшена по умолчанию будет выглядеть как http://localhost/Guestbook/Guestbook/Index.

Создание модуля на этом завершено :) Как его использовать? Очень просто. В основной проект добавляем референсы на модуль и MvcContrib (ну и Microsoft.Web.Mvc пригодится :)). Создаем папку Areas и копируем Web.config из папки Views в папку Areas.

Всё. Запускаем проект и заходим на localhost/Guestbook/Guestbook (первый guestbook — имя области, второй — имя контроллера).

На экшены из модуля можно также ставить ссылки и подключать их с использованием RenderAction — всё, как с обычными контроллерами.

@(Html.ActionLink<GuestbookController>(x => x.Index(), "строготипизированная ссылка на гостевую"));
@Html.ActionLink("нестроготипизированная ссылка на гостевую", "Index", "Guestbook", new { area = "Guestbook" }, null);


@{ Html.RenderAction<GuestbookController>(x => x.Index()); }
@{ Html.RenderAction("Index", "Guestbook", new { area = "Guestbook" }); }

Однако, чтобы строготипизированные ссылки работали, контроллеры внутри выделенной области надо обозначить атрибутом ActionLinkArea(«AreaName»):

 [ActionLinkArea("Guestbook")]
    public class GuestbookController : Controller
    {
    }

P.S. Ну и последний бонус для тех, кто хочет заставить это работать на linux под Mono.
На данный момент придется создать две папки Areas в структуре рабочего проекта на сервере — с большой буквы «A» и с маленькой. Необходимость в «маленькой» — баг со стороны библиотеки MvcContrib (она осуществляет предварительные проверки). Патч на этот баг отправлен разработчикам (патч в один символ :)).

Опубликовать в Facebook
Опубликовать в Google Plus

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *