使用自定义VirtualPathProvider加载embedded的资源部分视图

我编写了成功获取部分视图的embedded式资源的自定义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"); 

是什么使它相信这不是一种局部的看法?

编辑:我张贴我的代码为虚拟文件和虚拟文件提供程序的实现任何人谁在这寻找解决scheme获取该组件的工作。 这个问题对于那些基于问题标题的人来说也是很好的。

ere是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()); 

因为现在你正在从一些未知的位置提供你的视图,所以不再需要~/Views/web.config文件,它为你的剃刀视图提供了基类( <pages pageBaseType="System.Web.Mvc.WebViewPage"> )。 所以你可以在每个embedded视图的顶部添加@inherits指令来表示基类。

 @inherits System.Web.Mvc.WebViewPage @model ... 

我使用OP的答案作为基础,但扩展了一点,并在我的解决scheme中纳入了问题的答案。

这似乎是一个有点常见问题,所以我没有看到一个完整的答案,所以我认为这可能有助于分享我的工作解决scheme。

我从数据库加载我的资源,并将它们caching在默认caching(System.Web.Caching.Cache)中。

我最终做的是在我用来查找资源的KEY上创build一个自定义CacheDependency。 这样,每当我的其他代码使caching无效(在编辑等)上,该键上的caching依赖性被移除,并且VirtualPathProvider反过来使其caching失效,并且VirtualFile被重新加载。

我也改变了代码,以便它自动prependsinheritance语句,以便它不需要存储在我的数据库资源,我也自动prepend几个默认的使用语句,因为这个“视图”不是通过正常渠道加载,所以任何默认包括在你的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); } } 

希望这可以帮助!

我严重依赖OP中的信息以及Darin Dimitrov的答案,创build一个用于跨项目共享MVC组件的简单原型 。 虽然这些都非常有帮助,但是我还是遇到了一些额外的障碍,这些障碍在原型中就像使用@ model的共享视图一样。