Комментарии 19
Спасибо за статью. Пришел примерно к такому же решению, но пока без прекомпилированных вьюх. И информацию о плагине я бы не стал брать из сборки, так как вы будете вынуждены ее загрузить. Если брать манифест в файлике, то вы еще до загрузки сборки можете проверить, например, совместимость версий.
Еще классно можно подцепить модули в виде Nuget пакетов ;)
Еще классно можно подцепить модули в виде Nuget пакетов ;)
Спасибо.
Идея хорошая. Попробую обязательно как будет время.
Согласен, идея очень хорошая. Но я сейчас занимаюсь преобразованием компонентов в шаблоны. Базовую систему в шаблон и плагин в шаблон для ускорения разработки.
Если брать манифест в файлике, то вы еще до загрузки сборки можете проверить, например, совместимость версий
Идея хорошая. Попробую обязательно как будет время.
Еще классно можно подцепить модули в виде Nuget пакетов ;)
Согласен, идея очень хорошая. Но я сейчас занимаюсь преобразованием компонентов в шаблоны. Базовую систему в шаблон и плагин в шаблон для ускорения разработки.
А так же для плагинов можно использовать Managed Extensibility Framework.
MEF — мощный инструмент, но в контексте модульной системы для сайтов, на мой взгляд, слишком тяжеловесна.
Для Asp.Net приложения там возникают разные нюансы. Например в плане загрузки сборок — shadow coping сборок перед загрузкой приложения и пробинг их из приватной папки веб приложения. Плюс MEF не решает вопросов с ресурсами(js, css, cshtml).
А можно подробнее, чем не устроили Areas?
С помощью того же RazorGenerator они отлично выносятся в отдельные dll'ки и получается, как мне кажется, нечто очень похожее на ваш конечный результат. Или я что-то упускаю?
С помощью того же RazorGenerator они отлично выносятся в отдельные dll'ки и получается, как мне кажется, нечто очень похожее на ваш конечный результат. Или я что-то упускаю?
Области не устроили только тем, что разработка плагина с их помощью не может быть выделена в написание/тестирование отдельного MVC проекта, который в конечном итоге компилируется в один единственный файл. Но я могу ошибаться, прошу поправьте.
Спасибо за статью!
Кстати, вышеописанный подход очень (!!!) похож на существующую реализацию системы плагинов в nopCommerce (http://www.nopcommerce.com) и Umbraco (http://umbraco.com/). Я бы даже сказал, что Вы переписали эту систему плагинов оттуда, просто переименовав названия классов и немного упростив код.
Кстати, статью лучше назвать «Плагинная система на ASP.NET», а не «Плагинная система на MVC», так как все вышеописанное (кроме примера с Layout.cshtml) можно реализовать и в стандартных ASP.NET web forms
Кстати, вышеописанный подход очень (!!!) похож на существующую реализацию системы плагинов в nopCommerce (http://www.nopcommerce.com) и Umbraco (http://umbraco.com/). Я бы даже сказал, что Вы переписали эту систему плагинов оттуда, просто переименовав названия классов и немного упростив код.
Кстати, статью лучше назвать «Плагинная система на ASP.NET», а не «Плагинная система на MVC», так как все вышеописанное (кроме примера с Layout.cshtml) можно реализовать и в стандартных ASP.NET web forms
Честно говоря никогда не смотрел внутрь Umbraco и nopCommerce, поэтому не могу ничего сказать по поводу схожести. Как я уже написал, конечная система родилась в результате сбора информации в сети и создания конечного проекта.
Конечно, я потратил больше времени при таком подходе, но считаю, что сделал это не зря, потому что разобрался с подходом и его функционированием.
Конечно, я потратил больше времени при таком подходе, но считаю, что сделал это не зря, потому что разобрался с подходом и его функционированием.
Классно! В свое время, еще на ASP.NET 1.1 сделал загрузку сборок через Reflection с дополнительным функционалом к основному ядру сайта.
Модули конфигурировались в одну строчку в определенном хмл файле
type id='AbsType' manager='extman.EventFestDopInfoManager;EventDopInfo.dll'
Тем самым, базовый функционал можно было безгранично расширять, без перекомпиляции ядра.
В модуле переопределялись методы базовых классов из ядра. Добавлялись новые классы ну и т.д. Страницы (вьюхи в нотации MVC) правда, мы туда не складывали, но ничто не мешает это делать.
Так как технология Web Forms категорически не устраивала, только с приходом MVC появилась возможность перевести весь проект на новые фрэймвоки. Вообще конечно жалко, что не было MVC в то время не было. Web Forms вселяло беспросветную тоску. Пришлось выкручиваться и придумывать все с нуля.
Будем переводить сразу на 4.5.
Думаю этот пост мне поможет в этом.
Если абстрагироваться от MVC и вообще от ASP. NET. Подобная идея отлично подходит для построения любого модульного приложения.
Запускать модули как отдельный сайт это интересная идея. Для тестов подойдет, нужно попробовать.
Модули конфигурировались в одну строчку в определенном хмл файле
type id='AbsType' manager='extman.EventFestDopInfoManager;EventDopInfo.dll'
Тем самым, базовый функционал можно было безгранично расширять, без перекомпиляции ядра.
В модуле переопределялись методы базовых классов из ядра. Добавлялись новые классы ну и т.д. Страницы (вьюхи в нотации MVC) правда, мы туда не складывали, но ничто не мешает это делать.
Так как технология Web Forms категорически не устраивала, только с приходом MVC появилась возможность перевести весь проект на новые фрэймвоки. Вообще конечно жалко, что не было MVC в то время не было. Web Forms вселяло беспросветную тоску. Пришлось выкручиваться и придумывать все с нуля.
Будем переводить сразу на 4.5.
Думаю этот пост мне поможет в этом.
Если абстрагироваться от MVC и вообще от ASP. NET. Подобная идея отлично подходит для построения любого модульного приложения.
Запускать модули как отдельный сайт это интересная идея. Для тестов подойдет, нужно попробовать.
Что-то вы слишком усложняете. Проще надо быть, проще…
Узнать суть
Например:
//в целях упрощения примера пусть абсолютно все контроллеры будут плагинами. При этом в «плагине» может быть много контроллеров. Структура плагина — веб проект с стандартной asp.net mvc структурой каталогов — bin\ — бинарь, Views — вьюхи.
1) рисуем и тестируем свои контроллеры как обычно, но роуты проставляем атрибутами (для mvc4 используем AttributeRouting, для mvc5 — встроенный). Я буду рассматривать mvc5 — различий не особо много. Например HomeController в плагине HomePlugin выглядит так
2) пока забудем о Lazy загрузке контроллеров (для этого надо писать ControllerFactory, что в комменте не очень удобно, да и ничего в этом сложного). Будем регить плагины в веб конфиге хост-приложения.
Для этого пусть у нас есть еще и хост приложение. Опять же стандартный пустой MVC5 проект. Добавим авто регистрацию роутов по атрибутам:
(не забудем установить nuget пакет WebActivatorEx)
3) Наши плагины пусть будут копироваться в bin каталог хост-приложения (вместе с вьюхами). Т.е. структура хост приложения у нас будет такая: bin\HomePlugin\bin — бинари HomePlugin плагина, bin\HomePlugin\Views — вьюхи хоум плагина.
Регим загрузку нашего плагина хост приложением вписывая его в веб конфиг:
4) Запускаем и получаем болт в виде «не могу найти вьюху для HomeConteroller-а Index экшена.
Окей, это потому что они лежат не там где ожидает их RazorViewEngine — напшем свой ViewEngine и попутно его зарегим (это в хост приложении естественно):
и вуаля — у нас плагины и все дела… (для партиал вьюх аналогично, скрипты и прочее — через спец контроллер отдавайте из нужных каталогов).
//в целях упрощения примера пусть абсолютно все контроллеры будут плагинами. При этом в «плагине» может быть много контроллеров. Структура плагина — веб проект с стандартной asp.net mvc структурой каталогов — bin\ — бинарь, Views — вьюхи.
1) рисуем и тестируем свои контроллеры как обычно, но роуты проставляем атрибутами (для mvc4 используем AttributeRouting, для mvc5 — встроенный). Я буду рассматривать mvc5 — различий не особо много. Например HomeController в плагине HomePlugin выглядит так
namespace HomePlugin.Controllers
{
public class HomeController : Controller
{
[Route(""), HttpGet]
public ActionResult Index()
{
return View();
}
}
}
2) пока забудем о Lazy загрузке контроллеров (для этого надо писать ControllerFactory, что в комменте не очень удобно, да и ничего в этом сложного). Будем регить плагины в веб конфиге хост-приложения.
Для этого пусть у нас есть еще и хост приложение. Опять же стандартный пустой MVC5 проект. Добавим авто регистрацию роутов по атрибутам:
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(RouteConfig), "RegisterRoutes")]
namespace HomePlugin.App_Start
{
public class RouteConfig
{
public static void RegisterRoutes()
{
RouteCollection routes = RouteTable.Routes;
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
}
}
(не забудем установить nuget пакет WebActivatorEx)
3) Наши плагины пусть будут копироваться в bin каталог хост-приложения (вместе с вьюхами). Т.е. структура хост приложения у нас будет такая: bin\HomePlugin\bin — бинари HomePlugin плагина, bin\HomePlugin\Views — вьюхи хоум плагина.
Регим загрузку нашего плагина хост приложением вписывая его в веб конфиг:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.1">
<assemblies>
<add assembly="HomePlugin"/>
</assemblies>
</compilation>
<httpRuntime targetFramework="4.5.1" />
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin\HomePlugin\bin;" />
</assemblyBinding>
</runtime>
</configuration>
4) Запускаем и получаем болт в виде «не могу найти вьюху для HomeConteroller-а Index экшена.
Окей, это потому что они лежат не там где ожидает их RazorViewEngine — напшем свой ViewEngine и попутно его зарегим (это в хост приложении естественно):
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(RegisterViewEngine), "Start")]
namespace WebAndPlug.App_Start
{
public static class RegisterViewEngine
{
public static void Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new PluginViewEngine());
}
}
public class PluginViewEngine : RazorViewEngine
{
#region Overrides of VirtualPathProviderViewEngine
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
var ctrlAssembly = controllerContext.Controller.GetType().Assembly.GetName().Name;
string controller = controllerContext.RouteData.GetRequiredString("controller");
string action = controllerContext.RouteData.GetRequiredString("action");
viewName = "~/bin/" + ctrlAssembly + "/Views/" + controller + "/" + viewName + ".cshtml";
return base.FindView(controllerContext, viewName, masterName, useCache);
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
return base.FindPartialView(controllerContext, partialViewName, useCache);
}
#endregion
}
}
и вуаля — у нас плагины и все дела… (для партиал вьюх аналогично, скрипты и прочее — через спец контроллер отдавайте из нужных каталогов).
Мы с командой сейчас работаем с Orchard CMF.
Очень гибкая система, также построенная на Area (но специфичные). Но он тоже DLLки закидывает не в bin, а в App_Data. Но, если поковыряться, то уверен, можно будет переделать под свои нужды. Для загрузки модулей/плагинов используются реализации IExtensionLoader.
Очень гибкая система, также построенная на Area (но специфичные). Но он тоже DLLки закидывает не в bin, а в App_Data. Но, если поковыряться, то уверен, можно будет переделать под свои нужды. Для загрузки модулей/плагинов используются реализации IExtensionLoader.
Чтобы избежать копирования сборок перед загрузкой можно использовать AppDomain.CurrentDomain.SetShadowCopyPath и передать ему путь к каталогу со сборками. Этот метод помечен как Obsolete, но работает как надо.
Есть ли проблема с использованием Intellisense в видах?
И как обстоит дело с передачей модели в вид?
И как обстоит дело с передачей модели в вид?
Хотелось бы услышать за что минусуют статью
Трудно живется .NET программистам без OSGi :)
Недостатки текущей реализации которых не было бы в OSGi:
— общие ресусры
— общее пространство имен с базовым сайтом.
— недостаточный контроль зависимостей между плагинами (можно ли сделать один плагин зависящим от другого?)
— не понятно как реализовать взаимодействие плагинов между собой.
— необходимость перезапуска приложения для установки / удаления плагина.
Недостатки текущей реализации которых не было бы в OSGi:
— общие ресусры
— общее пространство имен с базовым сайтом.
— недостаточный контроль зависимостей между плагинами (можно ли сделать один плагин зависящим от другого?)
— не понятно как реализовать взаимодействие плагинов между собой.
— необходимость перезапуска приложения для установки / удаления плагина.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Плагинная система на ASP.NET. Или сайт с плагинами, мадемуазелями и преферансом