Пишем код

Заметки о .net разработке

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

without comments

Один из первых вопросов, которым я задался после знакомства с азами технологии 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. В простейшем случае он может выглядеть так:
    <br />
     public class GuestbookRegistration : PortableAreaRegistration<br />
        {<br />
            public override string AreaName<br />
            {<br />
                get { return "Guestbook"; }<br />
            }<br />
        }<br />
    

    Единственный необходимый параметр — это поле AreaName с названием области.
    Однако, здесь же можно и изменить маршруты для выделенной области:
    <br />
     public class GuestbookRegistration : PortableAreaRegistration<br />
        {<br />
            public override string AreaName<br />
            {<br />
                get { return "Guestbook"; }<br />
            }</p>
    <p>        public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)<br />
            {<br />
                base.RegisterArea(context, bus);</p>
    <p>            context.MapRoute(<br />
                   "Guestbook_Default",<br />
                   "GuestbookPro/{action}/{id}",<br />
                   new { controller = "Guestbook", action = "Index", id = UrlParameter.Optional });</p>
    <p>        }<br />
        }<br />
    

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

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

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

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

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

Однако, чтобы строготипизированные ссылки работали, контроллеры внутри выделенной области надо обозначить атрибутом ActionLinkArea(«AreaName»):
<br />
 [ActionLinkArea("Guestbook")]<br />
    public class GuestbookController : Controller<br />
    {<br />
    }<br />

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

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

Written by Shaddix

Июль 10th, 2011 at 11:02 пп

Posted in .net,MVC,web

Leave a Reply