2012-02-08 16 views
5

Estoy usando vistas compiladas Razor en bibliotecas de clases separadas como una especie de sistema de complemento para MVC3.MVC3 - Vista de navaja compilada no se puede encontrar _ViewStart

He seguido la guía de Chris Van De Steed here y solo me he desviado principalmente en la parte sobre cómo agregar referencias, ya que estoy cargando mis ensamblajes en tiempo de ejecución.

Debido a que estoy cargando ensamblajes en tiempo de ejecución, no estoy usando VirtualPathProviderViewEngine en la biblioteca de BoC, y en su lugar implementé mi propio ViewEngine basado en RazorViewEngine. Funciona reescribiendo el viewPath en CreateView para insertar el espacio de nombre apropiado para que la vista se pueda resolver.

Hasta ahora todo bien ... Puedo cargar diferentes módulos y sus controladores no colisionarán si comparten el mismo nombre.

El único problema que tengo ahora es que para las vistas compiladas, no se llama al _ViewStart. _ViewStart funciona para vistas en el proyecto host MVC3, pero para las vistas cargadas desde los ensamblados de complementos no se encuentra.

Tengo una configuración de ruta de esta manera: -

RouteTable.Routes.MapRoute(
    string.Format("Plugin{0}Route", pluginName), 
    string.Format(@"Plugin/{0}/{{controller}}/{{action}}", pluginName), 
    new { }, 
    new string[] { string.Format("{0}.Controllers", pluginName) }); 

la ViewEngine se ve así: -

public class PluginRazorViewEngine : RazorViewEngine 
{ 
    public PluginRazorViewEngine() : base() 
    { 
     ViewLocationFormats = new[] 
     { 
      "~/Plugin/%1/Views/{1}/{0}.cshtml", 
      "~/Plugin/%1/Views/{1}/{0}.vbhtml", 
      "~/Plugin/%1/Views/Shared/{0}.cshtml", 
      "~/Plugin/%1/Views/Shared/{0}.vbhtml", 
      "~/Views/{1}/{0}.cshtml", 
      "~/Views/{1}/{0}.vbhtml", 
      "~/Views/Shared/{0}.cshtml", 
      "~/Views/Shared/{0}.vbhtml" 
     }; 

(el% 1 se sustituye por el nombre de la asamblea)

y el conjunto está registrado en la biblioteca de BoC así: -

BoC.Web.Mvc.PrecompiledViews.ApplicationPartRegistry.Register(assembly, string.Format("/Plugin/{0}/", pluginName)); 

Cuando la vista se carga desde un ensamblaje de complemento (en este ejemplo "cuentas"), la vista se encuentra y se muestra en Aceptar. Pero entonces se ve en estas ubicaciones para el _ViewStart: -

~/plugin/accounts/views/invoice/_viewstart.cshtml 
~/plugin/accounts/views/invoice/_viewstart.vbhtml 
~/plugin/accounts/views/_viewstart.cshtml 
~/plugin/accounts/views/_viewstart.vbhtml 
~/plugin/accounts/_viewstart.cshtml 
~/plugin/accounts/_viewstart.vbhtml 
~/plugin/_viewstart.cshtml 
~/plugin/_viewstart.vbhtml 
~/_viewstart.cshtml 
~/_viewstart.vbhtml 

pero no se ve en ~/Vistas/Común/_ViewStart.cshtml donde vive el archivo.

He intentado cambiar todos los formatos de localización en mi ViewEngine (AreaMasterLocationFormats, AreaPartialViewLocationFormats, AreaViewLocationFormats, MasterLocationFormats, PartialViewLocationFormats y ViewLocationFormats) pero ninguno de ellos parecen hacer una diferencia.

He mirado alrededor y parece que System.Web.WebPages.StartPage.GetStartPage es responsable de encontrar y devolver la página de inicio en una vista, pero no puedo encontrar ninguna información sobre cómo controlar dónde se ve.

He intentado mover el _ViewStart.cshtml a ~/_ViewStart.cshtml (uno de los lugares que se ve) sin embargo obtener de inmediato: -

Unable to cast object of type 'ASP._Page__ViewStart_cshtml' to type 'System.Web.WebPages.StartPage'. 

que según lo que he leído, es porque _ViewStart necesita vivir en/Vistas

¿Puedo modificar dónde MVC busca un _ViewStart?

La biblioteca del Banco de Canadá implementa su propio IView, y llama a los siguientes: -

startPage = this.StartPageLookup(page, VirtualPathFactoryManagerViewEngine.ViewStartFileName, this.ViewStartFileExtensions); 

Pero en este caso es sólo ViewStartFileName "_ViewStart" y ViewStartFileExtensions son sólo cshtml y vbhtml ... nada que pueda controlar dónde MVC debería buscar el archivo.

Respuesta

1

una idea ... (como en, no han probado ¿funcionará ni idea.?)

Tal vez echar un vistazo a la herencia de RazorView (o sustituir por completo, teniendo en cuenta - como veremos ver y Estarás reescribiendo el único método que es el grueso de la clase).

RazorView es donde StartPage.GetStartPage se introduce a través de la asignación a una propiedad StartPageLookup:

// In RazorView constructor: 
StartPageLookup = StartPage.GetStartPage; 

Desafortunadamente, esa propiedad delegado es interna, por lo que no se puede simplemente sobrescribir en su constructor de clase derivada. Es posible, sin embargo, ser capaz de anular RazorView.RenderView, que es en donde está utiliza (código fuente MVC3, un montón de líneas eliminadas, saltos de línea añadidos por mí):

protected override void RenderView(ViewContext viewContext, TextWriter writer, 
    object instance) 
{ 
    // [SNIP] 

    WebPageRenderingBase startPage = null; 
    if (RunViewStartPages) { 
    // HERE IT IS: 
    startPage = StartPageLookup(
     webViewPage, 
     RazorViewEngine.ViewStartFileName, 
     ViewStartFileExtensions 
    ); 
    } 
    webViewPage.ExecutePageHierarchy(
    new WebPageContext(
     context: viewContext.HttpContext, 
     page: null, 
     model: null), 
    writer, startPage); 
} 

reemplazar esa llamada StartPageLookup con su propio búsqueda, luego reemplace el resultado de CreateView y CreatePartialView en su PluginRazorViewEngine con su nueva clase PluginRazorView.

+0

Hice como sugirió y creé mi propio "PluginRazorView" heredando de RazorView. Tuve que anular RenderView como sugirió e intenté replicar lo que hace el método MVC GetStartPage y simplemente agregar mi propia lógica para buscar las rutas adicionales, sin embargo, es un campo minado de internos y reflexión así que he pirateado algo para demostrar que funciona y tendré que limpiarlo más tarde. Es interesante que hayan usado un delegado aquí ... parece que se pueden usar diferentes métodos de búsqueda, pero no hay forma de especificarlos. Muchas gracias por la ayuda. – Aleks

+0

Sí, eso es lo que pensé también. Como dices, usan un delegado para permitir el cambio del método de búsqueda, pero luego lo declaran 'interno', por lo que solo el espacio de nombres de Mvc puede cambiarlo ... – JimmiTh

1

Por lo tanto, para responder a mi propia pregunta ... Parece que no, no se puede modificar cuando MVC busca el _ViewStart ...

En cuanto a la fuente de System.Web.WebPages.StartPage. GetStartPage (que obtuve de here) Puedo ver que solo cruza la ruta desde la página de llamada hasta la raíz, y no parece que haya ninguna forma de controlar este comportamiento (es decir, está codificado en System.Web.WebPages.StartPage)

En circunstancias normales (es decir, un diseño MVC3 estándar), esto estaría bien ... todas las vistas se ubican en/Vistas y también lo hace el archivo _ViewStart principal, que se evaluaría cuando se alcanzara GetStartPage eso.

Lo que básicamente he hecho es romper esta funcionalidad al mover mis vistas fuera de la jerarquía de carpetas/Vistas.

Supongo que esto significa que puedo mover mi archivo _ViewStart a un lugar donde lo encontraré (lo que rompe mi objetivo de tener un _ViewStart común para todos mis complementos) o encontrar alguna manera de volver a escribir/reorientación de las solicitudes de _ViewStart en esa jerarquía al archivo correcto en/Views/Shared/_ViewStart (que no es inmediatamente aparente para mí).

Lo que es interesante es que el código MVC3 buscará en _ViewStart /, que en base a lo que he leído no va a funcionar, lo que resulta en el No se puede convertir objeto de tipo" 'ASP. Página _ViewStart_cshtml' a escriba 'System.Web.WebPages.StartPage' "error (aunque sospecho que esto es solo porque el /web.config predeterminado no tiene las cosas necesarias para analizar correctamente el archivo, mientras que /Views/web.config sí lo hace)

Cuestiones relacionadas