asp.net mvc 3 - ventana - Usar VirtualPathProvider personalizado para cargar vistas parciales de recursos incrustados
vista parcial en modal mvc (3)
Escribí implementaciones personalizadas de VirtualFile y VirtualPathProvider que obtienen con éxito recursos incrustados que son vistas parciales.
Sin embargo, cuando intento renderizarlos produce este error:
The view at ''~/Succeed.Web/Succeed.Web.Controls.SImporter._SImporter.cshtml'' must derive from WebViewPage, or WebViewPage<TModel>.
Al renderizar la vista parcial, dentro de una vista normal, se ve como sigue:
Html.RenderPartial("~/Succeed.Web/Succeed.Web.Controls.SImporter._SImporter.cshtml");
¿Qué lo está haciendo creer que esta no es una vista parcial?
EDITAR: Publiqué mi código para las implementaciones de los proveedores de archivos virtuales y archivos virtuales para cualquier persona que tropiece con esta solución en busca de que ese componente funcione. Esta pregunta también servirá para aquellos basados en el título de la pregunta.
Existe la implementación de VirtualFile para referencia:
public class SVirtualFile : VirtualFile
{
private string m_path;
public SVirtualFile(string virtualPath)
: base(virtualPath)
{
m_path = VirtualPathUtility.ToAppRelative(virtualPath);
}
public override System.IO.Stream Open()
{
var parts = m_path.Split(''/'');
var assemblyName = parts[1];
var resourceName = parts[2];
assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName);
var assembly = System.Reflection.Assembly.LoadFile(assemblyName + ".dll");
if (assembly != null)
{
return assembly.GetManifestResourceStream(resourceName);
}
return null;
}
}
VirtualPathProvider:
public class SVirtualPathProvider : VirtualPathProvider
{
public SVirtualPathProvider()
{
}
private bool IsEmbeddedResourcePath(string virtualPath)
{
var checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
return checkPath.StartsWith("~/Succeed.Web/", StringComparison.InvariantCultureIgnoreCase);
}
public override bool FileExists(string virtualPath)
{
return IsEmbeddedResourcePath(virtualPath) || base.FileExists(virtualPath);
}
public override VirtualFile GetFile(string virtualPath)
{
if (IsEmbeddedResourcePath(virtualPath))
{
return new SVirtualFile(virtualPath);
}
else
{
return base.GetFile(virtualPath);
}
}
public override CacheDependency GetCacheDependency( string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
{
if (IsEmbeddedResourcePath(virtualPath))
{
return null;
}
else
{
return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}
}
}
Y, por supuesto, no olvide registrar este nuevo proveedor en el archivo Global.asax de su proyecto en el evento Application_Start ()
System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new SVirtualPathProvider());
Debido a que ahora está publicando sus vistas desde una ubicación desconocida, ya no existe el archivo ~/Views/web.config
que se aplica e indica la clase base para sus vistas de razor ( <pages pageBaseType="System.Web.Mvc.WebViewPage">
) De modo que podría agregar una directiva @inherits en la parte superior de cada vista incrustada para indicar la clase base.
@inherits System.Web.Mvc.WebViewPage
@model ...
Me apoyé mucho en la información del OP así como en la respuesta de Darin Dimitrov para crear un prototipo simple para compartir componentes de MVC en todos los proyectos. Si bien estos fueron muy útiles, todavía encontré algunas barreras adicionales que se abordan en el prototipo, como el uso de vistas compartidas con @modelos.
Utilicé la respuesta de OP como base, pero la amplié un poco e incorporé la respuesta a la pregunta en mi solución.
Esto parece una pregunta algo común aquí en SO y no he visto una respuesta completa, así que pensé que podría ser útil compartir mi solución de trabajo.
Cargué mis recursos de una base de datos y los guardo en la caché predeterminada (System.Web.Caching.Cache).
Lo que terminé haciendo fue crear una CacheDependency personalizada en la KEY que estoy usando para buscar el recurso. De esta forma, cada vez que mi otro código invalide ese caché (en una edición, etc.) se elimina la dependencia del caché de esa clave y el VirtualPathProvider a su vez invalida su caché y VirtualFile se vuelve a cargar.
También cambié el código para que automáticamente preceda hereda declaración para que no tenga que ser almacenado en mi recurso de base de datos y también antepongo algunas declaraciones de uso predeterminadas automáticamente ya que esta "vista" no se carga a través de los canales normales, por lo cualquier cosa predeterminada incluye en su web.config o viewstart no son utilizables.
CustomVirtualFile:
public class CustomVirtualFile : VirtualFile
{
private readonly string virtualPath;
public CustomVirtualFile(string virtualPath)
: base(virtualPath)
{
this.virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
}
private static string LoadResource(string resourceKey)
{
// Load from your database respository or whatever here...
// Note that the caching is disabled for this content in the virtual path
// provider, so you must cache this yourself in your repository.
// My implementation using my custom service locator that sits on top of
// Ninject
var contentRepository = FrameworkHelper.Resolve<IContentRepository>();
var resource = contentRepository.GetContent(resourceKey);
if (String.IsNullOrWhiteSpace(resource))
{
resource = String.Empty;
}
return resource;
}
public override Stream Open()
{
// Always in format: "~/CMS/{0}.cshtml"
var key = virtualPath.Replace("~/CMS/", "").Replace(".cshtml", "");
var resource = LoadResource(key);
// this automatically appends the inherit and default using statements
// ... add any others here you like or append them to your resource.
resource = String.Format("{0}{1}", "@inherits System.Web.Mvc.WebViewPage<dynamic>/r/n" +
"@using System.Web.Mvc/r/n" +
"@using System.Web.Mvc.Html/r/n", resource);
return resource.ToStream();
}
}
CustomVirtualPathProvider:
public class CustomVirtualPathProvider : VirtualPathProvider
{
private static bool IsCustomContentPath(string virtualPath)
{
var checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
return checkPath.StartsWith("~/CMS/", StringComparison.InvariantCultureIgnoreCase);
}
public override bool FileExists(string virtualPath)
{
return IsCustomContentPath(virtualPath) || base.FileExists(virtualPath);
}
public override VirtualFile GetFile(string virtualPath)
{
return IsCustomContentPath(virtualPath) ? new CustomVirtualFile(virtualPath) : base.GetFile(virtualPath);
}
public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
{
if (IsCustomContentPath(virtualPath))
{
var key = VirtualPathUtility.ToAppRelative(virtualPath);
key = key.Replace("~/CMS/", "").Replace(".cshtml", "");
var cacheKey = String.Format(ContentRepository.ContentCacheKeyFormat, key);
var dependencyKey = new String[1];
dependencyKey[0] = string.Format(cacheKey);
return new CacheDependency(null, dependencyKey);
}
return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}
public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
{
if (IsCustomContentPath(virtualPath))
{
return virtualPath;
}
return base.GetFileHash(virtualPath, virtualPathDependencies);
}
}
¡Espero que esto ayude!