CacheDependency和VirtualPathProvider用于路径依赖

时间:2014-07-28 14:34:48

标签: asp.net-mvc

我们在ASP.NET MVC 4设置中使用BundleTransformer库。我们的Web应用程序是一个相当薄的层,所有服务器逻辑都在后端服务中处理。

安装完成后,所有资源都将安装在文件系统上的Web应用程序旁边,但出于更新原因,我们需要能够从服务中提供JavaScript和CSS(LESS)等资源 - 然后它们将覆盖本地(文件系统)版本。

从本质上讲,如果从服务中获得,我们从那里提供所请求的资源。如果没有,我们将回退到文件系统并从那里提供文件。

这一切都像魅力一样,然后我们介绍了LESS和@import陈述,现在事情已经不再那么好了。

我们仍然希望在Http Cache中缓存LESS转换的结果,并且我们希望在依赖项发生更改时使该结果无效。 VirtualPathProvider中的当前实现可以做到这一点,但是如果我更新单个文件(例如JavaScript文件),它将不会更新。

我的VirtualPathProvider看起来像这样:

public class ViewsAndScriptsPathProvider : VirtualPathProvider {

    private static IApplicationServiceResourcesManager ResourceManager {
        get { return InstanceProvider.Get<IApplicationServiceResourcesManager>(); }
    }

    public override bool FileExists(string virtualPath) {
        var exists = ResourceManager.Exists(virtualPath);
        return exists || base.FileExists(virtualPath);
    }

    public override VirtualFile GetFile(string virtualPath) {
        VirtualFile file;
        if (ResourceManager.TryGet(virtualPath, out file)) {
            return file;
        }
        return base.GetFile(virtualPath);
    }

    public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) {
        bool isRelevant = ResourceManager.IsRelevant(virtualPath);
        if (isRelevant) {
            var cachekeys = virtualPathDependencies.Cast<string>().Where(dependency => virtualPath != dependency).ToArray();
            return new CacheDependency(null, cachekeys);
        }
        if (IsBundle(virtualPath)) {
            return new CacheDependency(null, new[] { ResourceManager.ComponentCacheKey }, utcStart);
        }
        return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
    }

    private bool IsBundle(string virtualPath) {
        return virtualPath.StartsWith("~/bundles/", StringComparison.InvariantCultureIgnoreCase)
               || virtualPath.StartsWith("~/css/", StringComparison.InvariantCultureIgnoreCase);
    }

    public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies) {
        byte[] bhash;
        string filehash;
        if (ResourceManager.TryGetFileHash(virtualPath, out bhash)) {
            filehash = BitConverter.ToString(bhash);
        } else {
            filehash = Previous.GetFileHash(virtualPath, virtualPathDependencies);
        }
        return filehash;
    }
}

将ResourceManager视为服务的代理/缓存。

我的大问题是我不完全了解CacheDependency的工作。如果我添加一个包含virtualPath本身的cachekey(第二个参数),那么我会在服务器中获得一个无限循环。 如果我只是返回null,那么它将无法为@imports工作。

如果有人可以解释或指出VirtualPathProvider应该如何实现GetCacheDependency和GetFileHash函数,我或许可以解决这个问题。

1 个答案:

答案 0 :(得分:1)

我实际上是在前一段时间制定了一个解决方案,它与HttpCache如何与CacheDependency对象一起工作有关。然而,它相当复杂。

基本上我有三种情况:

  1. 资源仅托管在文件系统上。
  2. 资源仅在服务中托管。
  3. 资源托管在服务和本地文件系统上。
  4. 对于1.我使用CacheDependency对象作为文件位置。这是标准的,默认情况下VirtualPathProvider的工作方式。

    对于2.我使用自定义(派生)ResourceCacheDependency,当代理具有新版本时,它实现逻辑以使自身无效。

    对于3.我使用AggregateCacheDependency对象,该对象同时具有物理文件的CacheDependency和ResourceCacheDependency对象。

    对于所有虚拟路径依赖项(资源的依赖项列表),我重复上述假设,并将其构建到AggreateCacheDependency中(它可能具有很多依赖项)。

    在我的VirtualPathProvider的自定义实现中,我重写了GetCacheDependency方法,以根据上述分析返回相关的CacheDependency对象。