MEF с MVC 4 или 5 – подключаемая архитектура (2014)

Я пытаюсь создать приложение MVC4 / MVC5 с подключаемой архитектурой, такой как Orchard CMS. Таким образом, у меня есть приложение MVC, которое будет проектом запуска и позаботится о auth, навигации и т. Д. Тогда будет несколько модhive, построенных отдельно как библиотеки classов asp.net или разделенные проекты mvc и имеющие controllerы, представления, репозитории данных и т. Д.

Я весь день проводил учебники в Интернете и загружал образцы и т. Д., И обнаружил, что у Кенни лучший пример – http://kennytordeur.blogspot.in/2012/08/mef-in-aspnet-mvc-4-and -webapi.html

Я могу импортировать controllerы из модhive (отдельные библиотеки DLL), если добавить ссылку на эти DLL. Но причина использования MEF заключается в возможности добавления модhive во время выполнения. Я хочу, чтобы библиотеки DLL вместе с представлениями были скопированы в каталог ~ / Modules // в стартовом проекте (мне это удалось), и MEF просто заберет их. Борьба, чтобы заставить MEF загружать эти библиотеки.

Есть также MefContrib, как описано в этом ответе ASP.NET MVC 4.0 Controllers и MEF, как свести эти два вместе? это следующее, что я собираюсь попробовать. Но я удивлен, что MEF не работает из коробки с MVC.

Кто-нибудь имеет похожую архитектуру, работающую (с MefContrib или без нее)? Первоначально я даже думал о том, чтобы скрыть Orchard CMS и использовать его в качестве frameworks, но он слишком сложный. Также было бы неплохо разработать приложение в MVC5, чтобы воспользоваться WebAPI2.

    Я работал над проектом с аналогичной архитектурой, подобной той, которую вы описали, и использовал те же технологии ASP.NET MVC и MEF . У нас было хост-приложение ASP.NET MVC, которое обрабатывало аутентификацию, авторизацию и все запросы. Наши плагины (модули) были скопированы в подпапку. Плагинами также были приложения ASP.NET MVC , в которых были свои собственные модели, controllerы, представления, css и js-файлы. Это шаги, которые мы выполнили, чтобы заставить его работать:

    Настройка MEF

    Мы создали движок на основе MEF который обнаруживает все составные части при запуске приложения и создает каталог составных частей. Это задача, которая выполняется только один раз при запуске приложения. Двигатель должен обнаружить все подключаемые компоненты, которые в нашем случае были расположены либо в папке bin главного приложения, либо в папке Modules(Plugins) .

     public class Bootstrapper { private static CompositionContainer CompositionContainer; private static bool IsLoaded = false; public static void Compose(List pluginFolders) { if (IsLoaded) return; var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"))); foreach (var plugin in pluginFolders) { var directoryCatalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", plugin)); catalog.Catalogs.Add(directoryCatalog); } CompositionContainer = new CompositionContainer(catalog); CompositionContainer.ComposeParts(); IsLoaded = true; } public static T GetInstance(string contractName = null) { var type = default(T); if (CompositionContainer == null) return type; if (!string.IsNullOrWhiteSpace(contractName)) type = CompositionContainer.GetExportedValue(contractName); else type = CompositionContainer.GetExportedValue(); return type; } } 

    Это пример кода classа, который выполняет обнаружение всех частей MEF. Метод Compose classа вызывается из метода Application_Start в файле Global.asax.cs . Для упрощения код сокращен.

     public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { var pluginFolders = new List(); var plugins = Directory.GetDirectories(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules")).ToList(); plugins.ForEach(s => { var di = new DirectoryInfo(s); pluginFolders.Add(di.Name); }); AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); Bootstrapper.Compose(pluginFolders); ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory()); ViewEngines.Engines.Add(new CustomViewEngine(pluginFolders)); } } 

    Предполагается, что все плагины копируются в отдельную подпапку в папке « Modules », которая находится в корневом каталоге хост-приложения. Каждая подпапка плагина содержит подпапку Views и dll из каждого плагина. В вышеприведенном методе Application_Start также инициализируется фабрика настраиваемого controllerа и настраиваемый механизм просмотра, который я буду определять ниже.

    Создание фабрики controllerов, которая считывает из MEF

    Вот код для определения фабрики пользовательских controllerов, который обнаружит controller, который должен обрабатывать запрос:

     public class CustomControllerFactory : IControllerFactory { private readonly DefaultControllerFactory _defaultControllerFactory; public CustomControllerFactory() { _defaultControllerFactory = new DefaultControllerFactory(); } public IController CreateController(RequestContext requestContext, string controllerName) { var controller = Bootstrapper.GetInstance(controllerName); if (controller == null) throw new Exception("Controller not found!"); return controller; } public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) { return SessionStateBehavior.Default; } public void ReleaseController(IController controller) { var disposableController = controller as IDisposable; if (disposableController != null) { disposableController.Dispose(); } } } 

    Кроме того, каждый controller должен быть отмечен атрибутом Export :

     [Export("Plugin1", typeof(IController))] [PartCreationPolicy(CreationPolicy.NonShared)] public class Plugin1Controller : Controller { // // GET: /Plugin1/ public ActionResult Index() { return View(); } } 

    Первый параметр конструктора атрибутов Export должен быть уникальным, поскольку он определяет имя контракта и однозначно идентифицирует каждый controller. Для параметра PartCreationPolicy должно быть установлено значение NonShared, поскольку controllerы не могут использоваться повторно для нескольких запросов.

    Создание механизма просмотра, который знает, как найти представления из плагинов

    Создание настраиваемого механизма просмотра необходимо, потому что механизм просмотра по конвенциям ищет представления только в папке « Views » хост-приложения. Поскольку плагины находятся в отдельной папке Modules , нам нужно также показать движку просмотра и посмотреть.

     public class CustomViewEngine : RazorViewEngine { private List _plugins = new List(); public CustomViewEngine(List pluginFolders) { _plugins = pluginFolders; ViewLocationFormats = GetViewLocations(); MasterLocationFormats = GetMasterLocations(); PartialViewLocationFormats = GetViewLocations(); } public string[] GetViewLocations() { var views = new List(); views.Add("~/Views/{1}/{0}.cshtml"); _plugins.ForEach(plugin => views.Add("~/Modules/" + plugin + "/Views/{1}/{0}.cshtml") ); return views.ToArray(); } public string[] GetMasterLocations() { var masterPages = new List(); masterPages.Add("~/Views/Shared/{0}.cshtml"); _plugins.ForEach(plugin => masterPages.Add("~/Modules/" + plugin + "/Views/Shared/{0}.cshtml") ); return masterPages.ToArray(); } } 

    Решите проблему с сильно типизированными представлениями в плагинах

    Используя только вышеуказанный код, мы не могли использовать строго типизированные представления в наших плагинах (модулях), потому что модели существовали вне папки bin . Чтобы решить эту проблему, выполните следующую ссылку .

    Просто имейте в виду, что контейнер MEF имеет «приятную функцию», которая поддерживает ссылки на любой объект IDisposable, который он создает, и приведет к огромной утечке памяти. Предположительно, утечка памяти может быть решена с помощью этого nuget – http://nuget.org/packages/NCode.Composition.DisposableParts.Cigned

    Существуют проекты, которые реализуют архитектуру плагина. Возможно, вы захотите использовать один из них или посмотреть исходный код, чтобы узнать, как они выполняют эти вещи:

    • Платформа ASP.NET MVC (с использованием MVC 4)
    • .NET 4.0 ASP.NET MVC 3 с встроенными представлениями (очевидно, с использованием MVC 3, но основные принципы могут все же применяться)

    Кроме того, 404 на controllerах во внешних assemblyх привлекает интересный подход. Я многому научился, просто прочитав вопрос.

    Давайте будем гением компьютера.