Vistas en conjuntos separados en ASP.NET MVC


48

Estoy tratando de crear una aplicación web en la que deseo poder conectar ensamblajes por separado. Estoy usando MVC preview 4 combinado con Unity para la inyección de dependencias, que utilizo para crear los controladores de mis ensamblajes de complementos. Estoy usando WebForms (aspx por defecto) como mi motor de visualización.

Si quiero usar una vista, estoy atascado en las que están definidas en el proyecto central, debido a la compilación dinámica de la parte ASPX. Estoy buscando una forma adecuada de adjuntar archivos ASPX en un ensamblaje diferente, sin tener que pasar por todo el paso de implementación. ¿Me estoy perdiendo algo obvio? ¿O debería recurrir a la creación de mis puntos de vista mediante programación?


Actualización: He cambiado la respuesta aceptada. Aunque la respuesta de Dale es muy completa, busqué la solución con un proveedor de ruta virtual diferente. Funciona como un amuleto, y toma solo unas 20 líneas en código, creo.

  0

¿El uso del proveedor de ruta de acceso virtual sigue trabajando con el enrutamiento en mvc.net? 24 oct. 082008-10-24 11:35:48

  0

@jmcd: parece que sí. 25 may. 092009-05-25 13:33:49

  0

¿Hay algún código de muestra que podría publicar que le permitió lograr esto? 11 ene. 102010-01-11 20:52:22

  0

El proyecto en el que lo probé se eliminó, por lo que no tengo el código de ejemplo listo. Sin embargo, el motor de vista de chispa también lo permite (http://www.sparkviewengine.com). Hay una muestra llamada módulos, creo, que separa las áreas en diferentes conjuntos. 11 ene. 102010-01-11 22:56:09

15

Básicamente este es el mismo problema que las personas tenían con WebForms y trataban de compilar sus archivos UserControl ASCX en una DLL. Encontré esto http://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspx que podría funcionar para usted también.


12
protected void Application_Start() 
{ 
    WebFormViewEngine engine = new WebFormViewEngine(); 

    engine.ViewLocationFormats = new[] { "~/bin/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx" }; 
    engine.PartialViewLocationFormats = engine.ViewLocationFormats; 

    ViewEngines.Engines.Clear(); 
    ViewEngines.Engines.Add(engine); 

    RegisterRoutes(RouteTable.Routes); 
} 

Establecer la propiedad 'Copiar a la salida' de la vista a 'Copiar siempre'

  0

En lugar de crear un WebFormviewEngine puede hacer lo mismo obteniendo el RazorViewEngine de esta manera: RazorViewEngine engine = ViewEngines.Engines.OfType <RazorViewEngine>(). First(); y luego solo concatena las rutas adicionales a las propiedades mencionadas. 30 nov. 142014-11-30 15:52:39


2

Una adición a todos los que todavía están buscando el Santo Grial: He venido un poco más cerca de encontrar si no estás demasiado apegado a la vista de webforms.

Recientemente probé la Spark viewengine. Además de ser totalmente increíble y no volvería a los formularios web, incluso si me amenazaban, también proporciona algunos ganchos muy buenos para la modularidad de una aplicación. El ejemplo en sus documentos está usando Windsor como un contenedor IoC, pero no puedo imaginar que sea mucho más difícil si quieres tomar otro enfoque.


29

Me tomó demasiado tiempo lograr que esto funcione correctamente desde varias muestras parciales, así que aquí está el código completo necesario para obtener vistas desde una carpeta Vistas en una biblioteca compartida estructurada igual que una carpeta Vistas normal pero con todo configurado para construir como recursos integrados. Solo usará el archivo incrustado si el archivo habitual no existe.

La primera línea de Application_Start:

HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedViewPathProvider()); 

El VirtualPathProvider

public class EmbeddedVirtualFile : VirtualFile 
{ 
    public EmbeddedVirtualFile(string virtualPath) 
     : base(virtualPath) 
    { 
    } 

    internal static string GetResourceName(string virtualPath) 
    { 
     if (!virtualPath.Contains("/Views/")) 
     { 
      return null; 
     } 



     var resourcename = virtualPath 
      .Substring(virtualPath.IndexOf("Views/")) 
      .Replace("Views/", "OrangeGuava.Common.Views.") 
      .Replace("/", "."); 

     return resourcename; 

    } 


    public override Stream Open() 
    { 
     Assembly assembly = Assembly.GetExecutingAssembly(); 


     var resourcename = GetResourceName(this.VirtualPath); 
     return assembly.GetManifestResourceStream(resourcename); 
    } 




} 

public class EmbeddedViewPathProvider : VirtualPathProvider 
{ 


    private bool ResourceFileExists(string virtualPath) 
    { 

     Assembly assembly = Assembly.GetExecutingAssembly(); 


     var resourcename = EmbeddedVirtualFile.GetResourceName(virtualPath); 
     var result = resourcename != null && assembly.GetManifestResourceNames().Contains(resourcename); 
     return result; 
    } 

    public override bool FileExists(string virtualPath) 
    { 
     return base.FileExists(virtualPath) || ResourceFileExists(virtualPath); 
    } 


    public override VirtualFile GetFile(string virtualPath) 
    { 

     if (!base.FileExists(virtualPath)) 
     { 
      return new EmbeddedVirtualFile(virtualPath); 
     } 
     else 
     { 
      return base.GetFile(virtualPath); 
     } 

    } 

} 

El paso final para que funcione es que la raíz web.config debe contener la configuración correcta para analizar inflexible MVC vistas, ya que la que está en la carpeta de vistas no se usará:

<pages 
    validateRequest="false" 
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
    userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> 
    <controls> 
    <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" /> 
    </controls> 
</pages> 

Se requieren algunos pasos adicionales para que funcione con Mono. En primer lugar, es necesario implementar GetDirectory, ya que todos los archivos en las vistas de carpetas se cargan cuando se inicia la aplicación en lugar de según sea necesario:

public override VirtualDirectory GetDirectory(string virtualDir) 
    { 
     Log.LogInfo("GetDirectory - " + virtualDir); 
     var b = base.GetDirectory(virtualDir); 
     return new EmbeddedVirtualDirectory(virtualDir, b); 
    } 

public class EmbeddedVirtualDirectory : VirtualDirectory 
{ 
    private VirtualDirectory FileDir { get; set; } 

    public EmbeddedVirtualDirectory(string virtualPath, VirtualDirectory filedir) 
     : base(virtualPath) 
    { 
     FileDir = filedir; 
    } 

    public override System.Collections.IEnumerable Children 
    { 
     get { return FileDir.Children; } 
    } 

    public override System.Collections.IEnumerable Directories 
    { 
     get { return FileDir.Directories; } 
    } 

    public override System.Collections.IEnumerable Files 
    { 
     get { 

      if (!VirtualPath.Contains("/Views/") || VirtualPath.EndsWith("/Views/")) 
      { 
       return FileDir.Files; 
      } 

      var fl = new List<VirtualFile>(); 

      foreach (VirtualFile f in FileDir.Files) 
      { 
       fl.Add(f); 
      } 


      var resourcename = VirtualPath.Substring(VirtualPath.IndexOf("Views/")) 
.Replace("Views/", "OrangeGuava.Common.Views.") 
.Replace("/", "."); 

      Assembly assembly = Assembly.GetExecutingAssembly(); 

      var rfl = assembly.GetManifestResourceNames() 
       .Where(s => s.StartsWith(resourcename)) 
       .Select(s => VirtualPath + s.Replace(resourcename, "")) 
       .Select(s => new EmbeddedVirtualFile(s)); 
      fl.AddRange(rfl); 

      return fl; 
     } 
    } 
} 

Finalmente, vistas inflexible casi pero no del todo funcione a la perfección.Modelo será tratada como un objeto sin tipo, así que para obtener la tipificación estricta posterior que necesita para empezar sus puntos de vista compartidos con algo como

<% var Model2 = Model as IEnumerable<AppModel>; %> 
+2

También es importante recordar establecer la acción de compilación en "Recurso incrustado" para la vista. La carpeta para la vista también debe existir en el proyecto web. En mi caso, pongo view en la carpeta del proyecto de ensamblaje Views/Shared/Dynamic.cshtml y tengo que crear Views/Shared en el proyecto web que hace referencia al ensamblado. 04 sep. 132013-09-04 06:38:30