Как стать автором
Обновить

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

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

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

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


Допустим, что у вас уже есть на сайте гостевая книга и есть желание повторно использовать её в другом проекте. Что надо сделать?
  1. Создать проект типа ClassLibrary и добавить в него референсы на System.Web.Mvc.dll, System.Web.Razor.dll (если вьюшки используют Razor), MvcContrib.dll и Microsoft.Web.Mvc.dll (последняя — для поддержки строгой типизации, можно пропустить).

  2. Создать структуру проекта, очень похожую на обычный MVC-шаблон. Единственное отличие, что папки Views, Controllers, etc. должны расположиться в корневой папке с именем нашей выделяемой области (portable area). В примере это — Guestbook. В данную структуру скопировать вьюшки и контроллеры, которые хочется обособить.

    Всем вьюшки в области необходимо выставить метод компиляции — embedded resource (как подсказывают в комментариях можно также копировать вьюшки в папку Areas основного проекта — MVC их там найдет)!

  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 (она осуществляет предварительные проверки). Патч на этот баг отправлен разработчикам (патч в один символ :)).

P.P.S. Удивлен, что тема абсолютно не освещалась на хабре, да и в рунете особенно ничего нет.

P.P.P.S. В комментариях затронули тему применимости T4MVC для «выделенных областей». После небольшого патча T4MVC замечательно работает. Детали интеграции можно прочитать по ссылке
Теги:
Хабы:
+15
Комментарии36

Публикации

Истории

Работа

.NET разработчик
75 вакансий

Ближайшие события