c# asp.net .net asp.net-mvc mef

c# - MEF con MVC 4 o 5-Arquitectura enchufable(2014)



asp.net .net (3)

Hay proyectos que implementan una arquitectura de complemento. Es posible que desee utilizar uno de estos o echar un vistazo a su código fuente para ver cómo logran estas cosas:

Además, 404 en Controladores en ensambles externos está tomando un enfoque interesante. Aprendí mucho con solo leer la pregunta.

Estoy intentando construir una aplicación MVC4 / MVC5 con una arquitectura conectable como Orchard CMS. Así que tengo una aplicación MVC que será el proyecto de inicio y me ocuparé de la autenticación, navegación, etc. Luego habrá múltiples módulos construidos por separado como bibliotecas de clase asp.net o proyectos de mvc simplificados y tendrán controladores, vistas, repositorios de datos, etc.

He pasado todo el día revisando tutoriales en la web y descargando muestras, etc. y descubrí que Kenny tiene el mejor ejemplo: http://kennytordeur.blogspot.in/2012/08/mef-in-aspnet-mvc-4-and-webapi.html

Puedo importar los controladores de los módulos (archivos DLL separados) si agrego referencia a esos archivos DLL. Pero la razón detrás de usar MEF es poder agregar módulos en tiempo de ejecución. Quiero que las DLL junto con las vistas se copien en un directorio ~ / Modules // en el proyecto de inicio (he logrado hacer esto) y MEF simplemente las recogería. Luchando para que MEF cargue estas bibliotecas.

También hay MefContrib como se explica en esta respuesta ASP.NET MVC 4.0 Controllers y MEF, ¿cómo juntar estos dos? que es lo siguiente que voy a intentar. Pero me sorprende que MEF no trabaje de la caja con MVC.

¿Alguien ha conseguido una arquitectura similar funcionando (con o sin MefContrib)? Inicialmente, incluso pensé en despojar a Orchard CMS y usarlo como marco, pero es demasiado complejo. También sería bueno desarrollar la aplicación en MVC5 para aprovechar WebAPI2.



He trabajado en un proyecto que tenía una arquitectura similar conectable como la que describiste y utilizaba las mismas tecnologías ASP.NET MVC y MEF . Tuvimos una aplicación Host ASP.NET MVC que manejó la autenticación, autorización y todas las solicitudes. Nuestros complementos (módulos) se copiaron a una subcarpeta de este. Los complementos también eran ASP.NET MVC que tenían sus propios modelos, controladores, vistas, archivos css y js. Estos son los pasos que seguimos para que funcione:

Configurando MEF

Creamos un motor basado en MEF que descubre todas las partes compostables al inicio de la aplicación y crea un catálogo de las partes compostables. Esta es una tarea que se realiza una sola vez al inicio de la aplicación. El motor debe descubrir todas las partes enchufables, que en nuestro caso estaban ubicadas en la carpeta bin de la aplicación host o en la carpeta Modules(Plugins) .

public class Bootstrapper { private static CompositionContainer CompositionContainer; private static bool IsLoaded = false; public static void Compose(List<string> 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<T>(string contractName = null) { var type = default(T); if (CompositionContainer == null) return type; if (!string.IsNullOrWhiteSpace(contractName)) type = CompositionContainer.GetExportedValue<T>(contractName); else type = CompositionContainer.GetExportedValue<T>(); return type; } }

Este es el código de muestra de la clase que realiza el descubrimiento de todas las partes de MEF. El método Compose de la clase se llama desde el método Application_Start en el archivo Global.asax.cs . El código se reduce por simplicidad.

public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { var pluginFolders = new List<string>(); 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)); } }

Se supone que todos los complementos se copian en una subcarpeta separada de la carpeta Modules que se encuentra en la raíz de la aplicación host. Cada subcarpeta de plugins contiene la subcarpeta Views y el dll de cada plugin. En el método Application_Start anterior, también se inicializan la fábrica de controlador personalizado y el motor de vista personalizado que definiré a continuación.

Creando una fábrica de controladores que lea desde el MEF

Aquí está el código para definir fábrica de controlador personalizado que descubrirá el controlador que necesita manejar la solicitud:

public class CustomControllerFactory : IControllerFactory { private readonly DefaultControllerFactory _defaultControllerFactory; public CustomControllerFactory() { _defaultControllerFactory = new DefaultControllerFactory(); } public IController CreateController(RequestContext requestContext, string controllerName) { var controller = Bootstrapper.GetInstance<IController>(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(); } } }

Además, cada controlador debe estar marcado con Export atributo Export :

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

El primer parámetro del constructor del atributo Export debe ser único porque especifica el nombre del contrato e identifica de forma única a cada controlador. PartCreationPolicy debe establecerse en NonShared porque los controladores no pueden reutilizarse para múltiples solicitudes.

Crear View Engine que sepa encontrar las vistas desde los complementos

Se necesita la creación de motor de vista personalizada porque el motor de vista por convención busca solo vistas en la carpeta Views de la aplicación de host. Dado que los complementos se encuentran en una carpeta de Modules separada, debemos indicar al motor de visualización que también lo haga.

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

Resuelva el problema con vistas fuertemente tipadas en los complementos

Al usar solo el código anterior, no pudimos usar vistas fuertemente tipadas en nuestros complementos (módulos), porque los modelos existían fuera de la carpeta bin . Para resolver este problema, siga el siguiente link .