我编写了自定义VirtualFile和VirtualPathProvider实现,这些实现成功获取了部分视图的嵌入式资源。
但是,当我尝试渲染它们时会产生此错误:
The view at '~/Succeed.Web/Succeed.Web.Controls.SImporter._SImporter.cshtml' must derive from WebViewPage, or WebViewPage<TModel>.
在常规视图内部渲染局部视图时,它看起来如下所示:
Html.RenderPartial("~/Succeed.Web/Succeed.Web.Controls.SImporter._SImporter.cshtml");
是什么导致它相信这不是局部观点?
编辑:我发布了我的虚拟文件和代码的代码;虚拟文件提供程序实现,适用于任何偶然发现这个问题的人在寻找解决方案。这个问题也适用于那些基于问题标题的人。
是VirtualFile实现的参考:
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);
}
}
}
当然,不要忘记在Application_Start()事件中的项目的Global.asax文件中注册这个新的提供程序
System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new SVirtualPathProvider());
答案 0 :(得分:44)
因为现在您正在从某个未知位置提供您的视图,所以不再应用~/Views/web.config
文件并指示您的剃刀视图的基类(<pages pageBaseType="System.Web.Mvc.WebViewPage">
)。因此,您可以在每个嵌入视图的顶部添加一个@inherits指令来指示基类。
@inherits System.Web.Mvc.WebViewPage
@model ...
答案 1 :(得分:6)
我使用OP的答案作为基础,但稍微扩展了一下,并在我的解决方案中纳入了问题的答案。
这似乎是一个有点常见问题的问题,我没有看到完整的答案,所以我认为分享我的工作解决方案可能会有所帮助。
我从数据库加载资源,并将它们缓存在默认的Cache(System.Web.Caching.Cache)中。
我最终做的是在我用来查找资源的KEY上创建自定义CacheDependency。这样,只要我的其他代码使该缓存无效(在编辑等上),就会删除对该密钥的缓存依赖性,并且VirtualPathProvider会使其缓存无效并重新加载VirtualFile。
我还更改了代码,以便它自动添加继承语句,以便它不需要存储在我的数据库资源中,并且我还自动添加一些默认的using语句,因为这个“视图”没有通过正常加载通道,因此web.config或viewstart中的任何默认包含都不可用。
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);
}
}
希望这有帮助!
答案 2 :(得分:0)
我非常依赖OP中的信息以及Darin Dimitrov的答案来创建一个simple prototype来跨项目共享MVC组件。虽然这些非常有帮助,但我还是遇到了原型中解决的一些其他障碍,比如使用与@ model的共享视图。