Один из первых вопросов, которым я задался после знакомства с азами технологии MVC3, это способ выделения и повторного использования функционала в нескольких веб-проектах.
В WPF или WinForms все просто и понятно — обособленный функционал изолируется в модуль, модуль компилируется в библиотеку, библиотека — подключается к проекту и повторно используется. Нужно лишь грамотно изолировать модули, и всё будет хорошо.
В MVC всё не так просто, если «обособленный функционал» — это набор контроллеров и вьюшек, реализующих, допустим, гостевую книгу, то просто так вынести их в отдельный модуль нельзя — MVC просто не найдет ваши контроллеры в соседних библиотеках.
Однако решение, конечно, есть, и его нам предлагает небезызвестная библиотека MvcContrib — open-source проект, неаффилированный Майкрософтом.
Изоляция нескольких взаимосвязанных контроллеров и вьюшек возможна в MVC3 и «из коробки» — для этого существуют так называемые «области» — areas. Однако вынести такие области в отдельный проект, на данный момент, без помощи MvcContrib нельзя.
Допустим, что у вас уже есть на сайте гостевая книга и есть желание повторно использовать её в другом проекте. Что надо сделать?
- Создать проект типа ClassLibrary и добавить в него референсы на System.Web.Mvc.dll, System.Web.Razor.dll (если вьюшки используют Razor), MvcContrib.dll и Microsoft.Web.Mvc.dll (последняя — для поддержки строгой типизации, можно пропустить).
-
Создать структуру проекта, очень похожую на обычный MVC-шаблон. Единственное отличие, что папки Views, Controllers, etc. должны расположиться в корневой папке с именем нашей выделяемой области (portable area). В примере это — Guestbook. В данную структуру скопировать вьюшки и контроллеры, которые хочется обособить.
Всем вьюшки в области необходимо выставить метод компиляции — embedded resource!
-
Создать регистрационный класс, который и позволит связать основное 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 (она осуществляет предварительные проверки). Патч на этот баг отправлен разработчикам (патч в один символ :)).