27

Tengo una aplicación compuesta de ASP.NET MVC 3 Razor usando MEF. Todo va bien si debo implementar complementos como archivos DLL y vistas (CSHTML) en la carpeta normal Views desde la aplicación. Pero esto no es muy limpio y no será un complemento real si no coloco las vistas como recursos incrustados dentro de los archivos DLL (junto con los controladores y los modelos).Vistas de MEF y Razor dentro de la biblioteca de clases

He seguido muchos artículos (la mayoría de ellos están desactualizados). De hecho, hay uno bastante bueno aquí en Stack Overflow: Controllers and Views inside a Class Library

También he comprobado documentos para VirtualPathProvider y he podido construir uno personalizado que encuentra el archivo dentro del ensamblaje y lo carga perfectamente (o al menos obtiene la corriente para ello). Para esto he seguido el VirtualPathProviderdocumentation on MSDN.

También hay una implementación para VirtualFile pero aún no para VirtualDirectory.

Aquí está el problema. Estoy trabajando con vistas de Razor. Sé que necesitan especificaciones de configuración del archivo web.config para que Razor las construya. Pero si los incrusto dentro de la DLL, esta configuración simplemente se pierde.

Me pregunto si es por eso sigo obteniendo el error:

The view at '~/Plugins/CRM.Web.Views.CRM.Index.cshtml' must derive from WebViewPage, or WebViewPage.

Tal vez sólo tiene que añadir algo de código para hacer que funcione? ¿Algunas ideas?

+1

Algo en qué pensar: si voy a agregar "@inherits System.Web.Mvc.WebViewPage" en cada archivo cshtml, todo va bastante bien. Sin embargo, no puedo simplemente hacer eso (sería un gran esfuerzo para algo que ya es automático utilizando archivos de vista regulares). Entonces, ¿alguna idea? –

+0

Esta publicación podría ser de alguna ayuda, pero simplemente no puedo probarla ahora. Lo intentaré lo antes posible y publicaré los resultados/respuesta aquí. http://stackoverflow.com/questions/6465855/a-plugin-framework-with-asp-net-mvc3-and-embedded-razor-views –

+0

¿Ya ha intentado poner también el archivo de configuración en una ruta virtual adecuada, de tal forma que simulemos la "disposición estándar" que tenemos en la carpeta Mvc View? –

Respuesta

3

Puede echar un vistazo al following blog post.

+0

Interesante. Pero esto requiere que precompile todas las vistas. No me gusta eso, simplemente quiero agregar el archivo cshtml dentro de la biblioteca de la clase y eso es todo (Recurso integrado y solo eso). ¡Otra cosa que creo que simplemente no podías ver es que ESTOY CONSTRUYENDO PLUGINS! Corrígeme si estoy equivocado pero nunca he visto la aplicación principal como un plugin que simplemente no sabe si el plugin existe o no. =) –

+0

@LordALMM, ¿por qué? ¿De qué manera la solución provista no cumple con sus requisitos en vistas de externalización en ensamblajes separados? –

+0

"Interesante. Pero esto requiere que precompile todas las vistas. No me gusta, simplemente quiero agregar el archivo cshtml dentro de la biblioteca de clases y eso es todo (Recurso incrustado y solo eso)". (Feb 15) ¿Funciona? Sí. Pero no es la mejor manera. El hecho de que puedas matar a una hormiga con una bomba no lo convierte en el mejor enfoque para la tarea. Estoy buscando una manera de obtener Razor y algún tipo de proveedor juntos para poder buscar un recurso incrustado (pero NO clases precompiladas, simplemente archivos CSHTML). –

2

Hossam,

El puesto que estamos hablando es lo que ya se ha sugerido Darin. La desventaja principal de ese enfoque es usar el compilador personalizado MvcRazorClassGenerator para convertir los archivos de vista CSHTML en archivos de clase. Para hacerlo, debe establecer cada vista CSHTML en su proyecto en Contenido y configurar la Herramienta personalizada en MvcRazorClassGenerator.

No puedo hablar de LordALMMa pero sí descargué la fuente del compilador y le di una oportunidad, y no funciona exactamente de la manera que esperaba.

Mi otro enfoque consiste en incluir los archivos CSHTML como Recursos Embeded en la DLL externa, leídos en los contenidos primas del archivo y ejecutar la vista como una cadena (Véase la RazorEngine en CodeProject para ver un ejemplo: http://razorengine.codeplex.com/)

No quería depender completamente del RazorEngine en una aplicación empresarial porque no sé qué tan bien es compatiable con toda la sintaxis de Razor, así que renuncié a eso por el momento.

Vengo de un prototipo que construí en ASP.NET MVC 2.0 que es una aplicación multi-tennant. En una granja de servidores, tenemos una instancia de una aplicación que se ejecuta donde todos los clientes comparten la misma base de código. En mi prototipo MVC 2.0, pude determinar para qué "cliente" se estaba haciendo la solicitud, buscar un controlador personalizado que sobrepasara la base (para personalizaciones del código central) y también buscar vistas personalizadas (para personalizaciones de la vista del núcleo). Lo que esto hace es permitirnos implementar un "complemento" por cada cliente. El software detecta si el cliente tiene un controlador personalizado que coincida con la solicitud, así como una acción personalizada que coincida y si lo hace, utiliza el controlador/acción personalizada en su lugar.

Cuando comencé a migrar mi prototipo a MVC 3 me encontré con el mismo problema que LordALMMa, el error "La vista en '... Index.cshtml' debe derivarse de WebViewPage o WebViewPage".Buscaré la ubicación de "@inherits System.Web.Mvc.WebViewPage" en mis vistas CSHTML y veré si eso me acerca más a que funcione.

Como tengo un prototipo MVC 2.0 que utiliza MVC 3 Razor no es una prioridad y no pierdo mucho tiempo en ello. Estoy seguro de que puedo portar el MVC 2.0 a MVC 3.0 utilizando el motor de WebForms si necesitamos aprovechar el Marco 4.0.

7

Mi forma preferida de incrustar Razor Views en una biblioteca de clases es copiarlas en las carpetas Vistas/Áreas del sitio web de MVC con un evento de compilación posterior. Se pueden especificar ubicaciones de vista personalizadas si anula ViewEngine o VirtualPathProvider.

La parte más difícil para mí fue hacer que intellisense funcionara en estas bibliotecas de View Class. Primero, debe agregar un Web.Config a su ensamblaje de Vista. Tenga en cuenta que no tiene que incluirlo en su conjunto. Solo tiene que estar en el directorio raíz del ensamblaje (o en la carpeta de vistas). Aquí hay un ejemplo. Consulte la sección de ensamblajes/compilación importante.

<?xml version="1.0"?> 
<configuration> 
    <configSections> 
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> 
     <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" /> 
     <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" /> 
    </sectionGroup> 
    </configSections> 

    <system.web.webPages.razor> 
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> 
    <pages pageBaseType="System.Web.Mvc.WebViewPage"> 
     <namespaces> 
     <add namespace="System.Web.Mvc" /> 
     <add namespace="System.Web.Mvc.Ajax" /> 
     <add namespace="System.Web.Mvc.Html" /> 
     <add namespace="System.Web.Routing" /> 
     </namespaces> 
    </pages> 
    </system.web.webPages.razor> 

    <appSettings> 
    <add key="webpages:Enabled" value="false" /> 
    </appSettings> 

    <system.web> 
    <compilation targetFramework="4.0"> 
     <assemblies> 
     <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
     <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
     <add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> 
     <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> 
     <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> 
     </assemblies> 
    </compilation> 

    <httpHandlers> 
     <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/> 
    </httpHandlers> 

    <!-- 
     Enabling request validation in view pages would cause validation to occur 
     after the input has already been processed by the controller. By default 
     MVC performs request validation before a controller processes the input. 
     To change this behavior apply the ValidateInputAttribute to a 
     controller or action. 
    --> 
    <pages 
     validateRequest="false" 
     pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
     pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
     userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> 
     <controls> 
     <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" /> 
     </controls> 
    </pages> 
    </system.web> 
    <system.webServer> 
    <validation validateIntegratedModeConfiguration="false" /> 
    <handlers> 
     <remove name="BlockViewHandler"/> 
     <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" /> 
    </handlers> 
    </system.webServer> 
</configuration> 

A continuación, resulta necesario modificar el archivo vbproj de la biblioteca de clases de manera que todos los elementos OutputPath apuntan a 'bin \' en lugar de 'depuración \ bin \' o 'Release \ bin \'. Esta es la principal diferencia que encontré entre las bibliotecas de clases y los tipos de proyectos web ASP.Net que pueden causar errores intellisense.

Si aún recibe su error de herencia obligatoria, considere usar @Inherits System.Web.Mvc.WebViewPage en sus vistas. Si no está copiando sus vistas en el proyecto de su sitio web, puede que las esté cargando desde Embedded Resources utilizando un ViewEngine/VirtualPathProvider personalizado. Si ese es el caso, definitivamente necesitas las Heredades para que Razor sepa cuál es desafortunadamente tu clase de base de vistas.

Buena suerte.

+0

Interesante. Lo probaré y publicaré los resultados. –

0

Oye, sospecho que tiene buenas razones para querer vistas dentro de las DLL. Sin embargo, también considere que es una forma inusual de empaquetar todo en una sola entidad.

Si está desarrollando un complemento, en la actualidad las personas optan por el embalaje en el formato NUGET, que también resuelve su tipo de problema, entre otras cosas. Tiene una estructura .nupkg que también es una forma de distribuir complementos como paquetes y bibliotecas.

Otra solución que las comunidades generalmente siguen (si no quieren algo tan complicado como Nuget) codifican las DLL de complementos de manera que no utiliza motores de vista como navajas de afeitar, sino que genera HTML por sí mismo utilizando la antigua forma primitiva de Response.Write y, por lo tanto, ser independiente de los archivos cshtml. Si aún desea usar cshtml, consulte this blog entry para precompilarlos en clases.

+0

Hmm Sé que la respuesta no es lo que la pregunta está buscando, pero mi punto es desalentar al que pregunta, de seguir esta manera de resolver su problema más profundo de empaque. – Zasz

Cuestiones relacionadas