no puede ver las vistas usando un tipo de modelo cargado por MEF. Estoy intentando crear un marco para permitir que los controladores y las vistas se importen dinámicamente en una aplicación MVC. Así es como funciona hasta el momento:ASP.NET MVC: el motor de vista
- estoy usando .NET 4, ASP.NET MVC 3 RC y la Navaja ViewEngine
- controladores se exportan e importan usando MEF por proyecto - que llamo un conjunto de controladores y las vistas de un proyecto determinado un "Módulo"
- Los ensamblajes descubiertos mediante MEF son referenciados dinámicamente por BuildManager usando un método de inicio de pre-aplicación y
BuildManager.AddReferencedAssembly
. - binarios (de proyecto de exportación) y las vistas son copiados en la estructura de carpetas del proyecto de destino utilizando un evento de acumulación
- controladores se seleccionan utilizando una fábrica de controlador personalizado que hereda de DefaultControllerFactory y anula GetControllerType()
- Vistas se seleccionan utilizando un motor de vista personalizada que hereda de RazorViewEngine y anula GetView() y GetPartialView() para permitir que se busque vistas en específicos del módulo vista directorios
todo funciona hasta ahora excepto de vistas utilizando un mo fuertemente tipado del. Las vistas que usan el modelo dinámico funcionan bien, pero cuando especifico un tipo de modelo usando @model
, obtengo un YSOD que dice "La vista 'Índice' o su maestro no se encontró".
Al depurar mi aplicación ViewEngine, puedo ver que: this.VirtualPathProvider.FileExists(String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller")))
vuelve verdadera, mientras que
this.FileExists(controllerContext, String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller")))
vuelve falsa.
Buscando en el reflector, la puesta en práctica de RazorViewEngine FileExists()
en última instancia termina haciendo esto:
return (BuildManager.GetObjectFactory(virtualPath, false) != null);
Sin embargo, no puedo ver BuildManager.GetObjectFactory()
del reflector porque está escondida alguna manera.
Sospecho que tiene algo que ver con el hecho de que el tipo de modelo es un tipo que se carga desde MEF, pero como ya estoy haciendo referencia a los ensamblados descubiertos por MEF de BuildManager, me he quedado sin conduce. ¿Alguien puede darnos un poco más de información sobre lo que podría estar pasando?
Actualización: Resulta que yo estaba usando una versión obsoleta del reflector de delante de .NET 4. Puedo ver GetObjectFactory() ahora, pero me parece que no puede realmente encontrar algo útil. He intentado añadir esto en mi FindView() Sobrecarga:
tratar {var path = String.Format (this.ViewLocationFormats [2], viewName, controllerContext.RouteData.GetRequiredString ("controlador")); var objFactory = System.Web.Compilation.BuildManager.GetObjectFactory (virtualPath: ruta, throwIfNotFound: true); } captura {}
Desafortunadamente, objFactory
termina nula, y no es una excepción es lanzada.Todos los bits que se ocupan de los errores de compilación son parte de métodos o tipos privados, por lo que no puedo depurar nada de eso, pero incluso parece que terminan arrojando una excepción, lo que no parece estar sucediendo. Parece que estoy en un callejón sin salida otra vez. ¡Ayuda!
Actualización 2
he descubierto que en el punto donde se está llamando FindView(), si llamo AppDomain.CurrentDomain.GetAssemblies()
, la asamblea que el tipo de modelo se encuentra en es incluido. Sin embargo, no puedo cargar el tipo usando Type.GetType()
.
Actualización 3
Aquí es lo que estoy viendo:
Update 4
Aquí la implementación ViewEngine:
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Hosting;
using System.Web.Compilation;
namespace Site.Admin.Portal
{
public class ModuleViewEngine : RazorViewEngine
{
private static readonly String[] viewLocationFormats = new String[]
{
"~/Views/{0}/{{1}}/{{0}}.aspx",
"~/Views/{0}/{{1}}/{{0}}.ascx",
"~/Views/{0}/{{1}}/{{0}}.cshtml",
"~/Views/{0}/Shared/{{0}}.aspx",
"~/Views/{0}/Shared/{{0}}.ascx",
"~/Views/{0}/Shared/{{0}}.cshtml"
};
public ModuleViewEngine(IModule module)
{
this.Module = module;
var formats = viewLocationFormats.Select(f => String.Format(f, module.Name)).ToArray();
this.ViewLocationFormats = formats;
this.PartialViewLocationFormats = formats;
this.AreaViewLocationFormats = formats;
this.AreaPartialViewLocationFormats = formats;
this.AreaMasterLocationFormats = formats;
}
public IModule Module { get; private set; }
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, String partialViewName, Boolean useCache)
{
var moduleName = controllerContext.RouteData.GetRequiredString("module");
if (moduleName.Equals(this.Module.Name, StringComparison.InvariantCultureIgnoreCase))
{
return base.FindPartialView(controllerContext, partialViewName, useCache);
}
else return new ViewEngineResult(new String[0]);
}
public override ViewEngineResult FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache)
{
var moduleName = controllerContext.RouteData.GetRequiredString("module");
if (moduleName.Equals(this.Module.Name, StringComparison.InvariantCultureIgnoreCase))
{
var baseResult = base.FindView(controllerContext, viewName, masterName, useCache);
return baseResult;
}
else return new ViewEngineResult(new String[0]);
}
}
}
BuildManager.GetObjectFactory no está oculto (es público, en realidad), pero es nuevo para .NET 4. Asegúrese de haber abierto las bibliotecas .NET correctas en Reflector. –
Oh, lo tengo. Actualizado. –
GetObjectFactory no lanza normalmente; es por eso que lo usamosEl hecho de que devuelva nulo generalmente significa que no se encontró el archivo. –