asp.net mvc 3 - 使用自定義VirtualPathProvider加載嵌入式資源部分視圖




asp.net-mvc-3 partial-views (2)

因為現在您正在從某個未知位置提供視圖,所以不再應用~/Views/web.config文件並指示剃刀視圖的基類( <pages pageBaseType="System.Web.Mvc.WebViewPage"> )。 因此,您可以在每個嵌入視圖的頂部添加一個@inherits指令來指示基類。

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

我編寫了自定義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");

是什麼讓它相信這不是局部觀點?

編輯:我為虛擬文件和虛擬文件提供程序實​​現發布了我的代碼,適用於任何偶然發現這個問題的人在尋找解決方案。 這個問題也適用於基於問題標題的人。

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

我使用OP的答案作為基礎,但稍微擴展了一下,並在我的解決方案中納入了問題的答案。

這似乎是關於SO的一個常見問題,我沒有看到完整的答案所以我認為分享我的工作解決方案可能會有所幫助。

我從數據庫加載我的資源,並將它們緩存在默認的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);
    }
}

希望這可以幫助!







virtualpathprovider