11

Estoy construyendo una aplicación MVC 3 donde se usa MEF. La idea principal es tener un mecanismo plug-in donde los modelos, controladores y vistas se carguen dinámicamente durante el tiempo de ejecución desde el contenedor mef.MEF y MVC 3: cómo cargar vistas incrustadas dinámicamente desde el contenedor mef?

Cada plugin/módulo consiste en dos conjuntos:

  • Module1.Data.dll (contiene definiciones de modelos)
  • Module1.Web.dll (Contiene controladores y vistas)

y se ponen en el directorio de plugins dentro de aplicaciones web bin:

  • aplicación web/bin/plugins/Module1.Data.dll
  • aplicación web/Bin/plugins/Module1.Web.dll
  • aplicación web/Bin/plugins/Module2.Data.dll
  • aplicación web/Bin/plugins/Module2.Web.dll
  • aplicación web/bin/plugins /ModuleCore.Data.dll
  • aplicación web/bin/plugins/ModuleCore.Web.dll
  • etc ...

también hay módulo principal al que hace referencia el resto de módulos: ModuleCore.Data. dll y respectivamente ModuleCore.Web.dll.

Luego, en Global.asax, contenedor está construido de la siguiente manera:

AggregateCatalog catalog = new AggregateCatalog(); 
var binCatalog = new DirectoryCatalog(HttpRuntime.BinDirectory, "Module*.dll"); 
var pluginsCatalot = new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins"), "Module*.dll"); 
catalog.Catalogs.Add(binCatalog); 
catalog.Catalogs.Add(pluginsCatalot); 
CompositionContainer container = new CompositionContainer(catalog); 
container.ComposeParts(this); 
AppDomain.CurrentDomain.AppendPrivatePath(Path.Combine(HttpRuntime.BinDirectory, "Plugins")); 

CustomViewEngine se crea y se registró y se usó para vistas encontrar en conjunto del módulo:

ViewEngines.Engines.Clear(); 
ViewEngines.Engines.Add(new CustomViewEngine()); 

controlador fábrica para cargar controladores desde el contenedor:

ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(_container)); 

y también proveedor personalizado de ruta virtual para obtener ensamblajes del recipiente:

HostingEnvironment.RegisterVirtualPathProvider(new ModuleVirtualPathProvider()); 

Ok, por lo que toda la infraestructura para el manejo de modelos enchufables, controladores y vistas están listos. Ahora todo funciona ... excepto una cosa: vistas fuertemente tipadas.

Para ilustrar el problema con más detalle, vamos a preparar la escena:

  • modelo UserDTO se encuentra en Module1.Data.dll
  • ShowUserController.cs se encuentra en Module1.Web.dll/Controladores/
  • Index.cshtml se encuentra en Module1.Web.dll/Views/ShowUser (con el @model Module1.Data declarado.UserDto)

Ahora vamos a hacer lo siguiente:

  1. Ejecutar la aplicación y vaya a HOST/ShowUser/Index (Índice de método de acción se ejecuta en ShowUserController y ver Index.cshtml es inverosímil)
  2. después de la vista Index.cshtml es inverosímil - compilación comienza (por RazorBuildProvider)
  3. excepciones se lanza: "no puede encontrar el tipo de datos en el espacio de nombres de módulo 1", en otras palabras UserDTO no se ha encontrado durante la construcción de la vista dinámicamente

Parece que el compilador/constructor no miró a través de la carpeta bin/Plugins para Module1.Data.dll, porque cuando copié este archivo en la carpeta bin, funcionaba bien.

pregunta/problema: qué constructor no se veía en la carpeta bin/plugins a pesar de que este directorio se añadió por el método AppDomain.CurrentDomain.AppendPrivatePath? ¿Cómo agregar rutas de acceso privadas para el ensamblador una vez para que la carpeta de complementos se tenga en cuenta?

que han logrado hacer un trabajo en torno a la creación de CustomRazorBuildProvider que anula estándar de un:

public class CustomRazorBuildProvider : RazorBuildProvider 
{ 
    public override void GenerateCode(System.Web.Compilation.AssemblyBuilder assemblyBuilder) 
    { 
    Assembly a = Assembly.LoadFrom(Path.Combine(HttpRuntime.BinDirectory, "Plugins", "Module1.Data.dll")); 
    assemblyBuilder.AddAssemblyReference(a);  
    base.GenerateCode(assemblyBuilder); 
    } 
} 

pero el inconveniente de esta solución es que cada vez que se compila la vista, las referencias a todos los montajes de la carpeta Plugins necesitan agregarse, lo que puede ocasionar problemas de rendimiento más adelante cuando se utilicen muchos complementos.

¿Alguna solución más agradable?

+2

¿Alguna vez resolvió esto? Estoy tratando de resolver el mismo problema con mi aplicación MVC en este momento. ¿Tiene alguna fuente en ejecución que yo pueda ver? – Coppermill

+1

Sí, lo resolví por CustomRazorBuildProvider como se describe arriba. Hovewer desde ese momento, nuestra aplicación se está volviendo cada vez más en el enfoque de MVVM, donde se usan menos vistas de la maquinilla de afeitar, y se construyen más vistas html/javascript. – untoldex

Respuesta

1

Aquí hay una idea.

Si sigue el patrón del modelo de visualización, en lugar de enviar las DTO directamente a la vista, utilice un modelo de vista que se ubicaría en el mismo conjunto que la vista.

Así que en lugar de:

modelo UserDTO se encuentra en Module1.Data.dll ShowUserController.cs se encuentra en Module1.Web.dll/Controladores/ Index.cshtml se encuentra en Module1.Web.dll/vistas/ShowUser (con declarada @Model Module1.Data.UserDto)

Usted tendrían:

modelo UserDTO se encuentra en Module1.Data.dll ShowUserController.cs se encuentra en Module1.Web.dll/Controladores/ UserVM ubicado en Module1.Web.d LL/ViewModels Index.cshtml se encuentra en Module1.Web.dll/Vistas/ShowUser (con declarada @Model Module1.Web.ViewModels.UserVM)

Haga que el Mapa Controlador de sus DTO a ViewModels

Ver AutoMapper para ayudar con el mapeo