36

我编写了自定义的 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;
    }
}

虚拟路径提供者:

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());
4

3 回答 3

45

因为现在您正在从某个未知位置提供您的视图,所以不再有~/Views/web.config适用的文件并指示您的剃刀视图 ( <pages pageBaseType="System.Web.Mvc.WebViewPage">) 的基类。因此,您可以在每个嵌入式视图的顶部添加一个 @inherits 指令来指示基类。

@inherits System.Web.Mvc.WebViewPage
@model ...
于 2011-10-12T20:59:44.260 回答
7

我使用 OPs 的答案作为基础,但对其进行了扩展,并将问题的答案合并到我的解决方案中。

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

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

我最终做的是在用于查找资源的 KEY 上创建自定义 CacheDependency。这样,每当我的其他代码使该缓存无效时(在编辑等时),对该键的缓存依赖就会被删除,而 VirtualPathProvider 反过来会使其缓存无效并且 VirtualFile 会被重新加载。

我还更改了代码,以便它自动添加继承语句,这样它就不需要存储在我的数据库资源中,并且我还自动添加了一些默认的 using 语句,因为这个“视图”不是通过正常通道加载的,所以您的 web.config 或 viewstart 中包含的任何默认值均不可用。

自定义虚拟文件:

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);
    }
}

希望这可以帮助!

于 2013-08-04T21:28:32.290 回答
1

我非常依赖 OP 中的信息以及 Darin Dimitrov 的回答来创建一个简单的原型,用于跨项目共享 MVC 组件。虽然这些非常有帮助,但我仍然遇到了一些在原型中解决的额外障碍,例如使用 @model 的共享视图。

于 2015-03-18T16:10:09.080 回答