代码之家  ›  专栏  ›  技术社区  ›  Jamie Treworgy

如何强制BundleCollection刷新MVC4中缓存的脚本捆绑包

  •  87
  • Jamie Treworgy  · 技术社区  · 13 年前

    我是如何学会不再担心,只针对微软完全没有文档的API编写代码的 .是否有该官员的任何实际文件 System.Web.Optimization 释放?'因为我肯定找不到任何东西,没有XML文档,所有的博客帖子都引用了与之大不相同的RC API。无论如何。。

    我正在编写一些代码来自动解析javascript依赖项,并根据这些依赖项动态创建捆绑包。一切都很好,除非您在不重新启动应用程序的情况下编辑脚本或进行会影响捆绑包的更改,否则这些更改不会得到反映。所以我添加了一个选项来禁用依赖项的缓存,以便在开发中使用。

    然而,显然 BundleTables 缓存URL 即使捆绑包集合已更改 例如,在我自己的代码中,当我想重新创建捆绑包时,我会这样做:

    // remove an existing bundle
    BundleTable.Bundles.Remove(BundleTable.Bundles.GetBundleFor(bundleAlias));
    
    // recreate it.
    var bundle = new ScriptBundle(bundleAlias);
    
    // dependencies is a collection of objects representing scripts, 
    // this creates a new bundle from that list. 
    
    foreach (var item in dependencies)
    {
        bundle.Include(item.Path);
    }
    
    // add the new bundle to the collection
    
    BundleTable.Bundles.Add(bundle);
    
    // bundleAlias is the same alias used previously to create the bundle,
    // like "~/mybundle1" 
    
    var bundleUrl = BundleTable.Bundles.ResolveBundleUrl(bundleAlias);
    
    // returns something like "/mybundle1?v=hzBkDmqVAC8R_Nme4OYZ5qoq5fLBIhAGguKa28lYLfQ1"
    

    每当我删除&重新创建捆绑包 使用相同的别名 ,绝对没有发生任何事情: bundleUrl 从返回 ResolveBundleUrl 与我删除&重新创建了捆绑包。我所说的“相同”是指内容哈希不变,以反映捆绑包的新内容。

    编辑 ……事实上,情况比这糟糕得多。这个 捆本身 以某种方式缓存在 Bundles 收集如果我只是生成自己的随机哈希以防止浏览器缓存脚本,ASP.NET将返回 旧剧本 。所以,很明显,从 BundleTable.Bundles 实际上什么也没做。

    我可以简单地更改别名来解决这个问题,这对开发来说是可以的,但我不喜欢这样的想法,因为这意味着要么我必须在每次页面加载后弃用别名,要么BundleCollection的大小随着每次页面加载而增加。如果您在生产环境中保留此选项,那将是一场灾难。

    因此,当脚本被提供时,它似乎会被缓存,而与实际情况无关 BundleTables.Bundles 对象因此,如果您重用一个URL,即使您在重用它之前删除了它引用的捆绑包,它也会用缓存中的任何内容进行响应,并更改 对象不刷新缓存--因此仅 项目(或者更确切地说,具有不同名称的新项目)将被使用。

    这种行为似乎很奇怪。。。从集合中删除某些内容应该会将其从缓存中删除。但事实并非如此。必须有一种方法来刷新此缓存,并使其使用 BundleCollection 而不是第一次访问该捆绑包时缓存的内容。

    你知道我该怎么做吗?

    有这个 ResetAll 方法,它有一个未知的目的,但它无论如何都会破坏东西,所以不是这样。

    6 回复  |  直到 13 年前
        1
  •  34
  •   Hao Kung    13 年前

    我们听到了您在文档方面的痛苦,不幸的是,该功能的变化仍然很快,生成文档有一些滞后,而且可能会立即过时。 Rick's blog post 是最新的,同时我也试图在这里回答问题以传播当前信息。我们目前正在建立我们的官方codeplex网站,该网站将始终提供最新的文档。

    现在,关于如何从缓存中清除捆绑包的具体问题。

    1. 我们使用从请求的捆绑url生成的密钥将捆绑响应存储在ASP.NET缓存中,即。 Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"] 我们还针对用于生成该捆绑包的所有文件和目录设置了缓存依赖关系。因此,如果任何底层文件或目录发生更改,缓存条目将被刷新。

    2. 我们并不真正支持按请求实时更新BundleTable/BundleCollection。完全支持的场景是在应用程序启动期间配置捆绑包(这样,在web场场景中一切都能正常工作,否则,如果发送到错误的服务器,一些捆绑包请求最终会变成404)。看看您的代码示例,我猜想您正试图根据特定请求动态修改捆绑包集合?任何类型的捆绑包管理/重新配置都应该伴随着appdomain重置,以确保一切都已正确设置。

    因此,避免在不回收应用程序域的情况下修改捆绑包定义。您可以自由修改捆绑包中的实际文件,这些文件应该会被自动检测到,并为捆绑包URL生成新的哈希代码。

        2
  •  21
  •   LeftyX    13 年前

    我也有类似的问题。
    在我班上 BundleConfig 我试着看看使用的效果如何 BundleTable.EnableOptimizations = true

    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            BundleTable.EnableOptimizations = true;
    
            bundles.Add(...);
        }
    }
    

    一切都很好。
    在某个时刻,我正在进行一些调试,并将属性设置为false。
    我很难理解发生了什么,因为jquery的捆绑包(第一个)似乎无法解析和加载( /bundles/jquery?v= )。

    骂了几句之后,我想(?!)我已经把事情解决了。 尝试添加 bundles.Clear() bundles.ResetAll() 在开始注册时,事情应该会重新开始工作。

    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Clear();
            bundles.ResetAll();
    
            BundleTable.EnableOptimizations = false;
    
            bundles.Add(...);
        }
    }
    

    我已经意识到只有当我更改 EnableOptimizations 所有物

    更新:

    深入挖掘我发现了 BundleTable.Bundles.ResolveBundleUrl @Scripts.Url 似乎在解决束路径方面存在问题。

    为了简单起见,我添加了一些图片:

    image 1

    我已经关闭了优化,并捆绑了一些脚本。

    image 2

    同一束包含在主体中。

    image 3

    @脚本Url 给了我束的“优化”路径,而 @Scripts.Render 生成正确的。
    同样的事情也发生在 BundleTable.Bundles.ResolveBundleUrl绑定表

    我使用的是Visual Studio 2010+MVC 4+Framework.Net 4.0。

        3
  •  8
  •   Zac    11 年前

    考虑到郝功建议不要这样做,因为网络农场场景,我认为有很多场景你可能想这样做。以下是一个解决方案:

    BundleTable.Bundles.ResetAll(); //or something more specific if neccesary
    var bundle = new Bundle("~/bundles/your-bundle-virtual-path");
    //add your includes here or load them in from a config file
    
    //this is where the magic happens
    var context = new BundleContext(new HttpContextWrapper(HttpContext.Current), BundleTable.Bundles, bundle.Path);
    bundle.UpdateCache(context, bundle.GenerateBundleResponse(context));
    
    BundleTable.Bundles.Add(bundle);
    

    您可以随时调用上述代码,您的捆绑包将得到更新。当EnableOptimization为true或false时,这两种方法都有效——换句话说,这将在调试或实时场景中抛出正确的标记,其中:

    @Scripts.Render("~/bundles/your-bundle-virtual-path")
    
        4
  •  4
  •   FriendScottN    10 年前

    我还遇到了在没有重建的情况下更新捆绑包的问题。以下是需要理解的重要事项:

    • 如果文件路径发生更改,则捆绑包不会得到更新。
    • 如果捆绑包的虚拟路径发生更改,则捆绑包确实会更新。
    • 如果磁盘上的文件发生更改,捆绑包确实会更新。

    因此,如果您正在进行动态绑定,您可以编写一些代码,使绑定的虚拟路径基于文件路径。我建议对文件路径进行散列,并将该散列附加到捆绑包的虚拟路径的末尾。这样,当文件路径发生更改时,虚拟路径和捆绑包也会随之更新。

    以下是我最终解决问题的代码:

        public static IHtmlString RenderStyleBundle(string bundlePath, string[] filePaths)
        {
            // Add a hash of the files onto the path to ensure that the filepaths have not changed.
            bundlePath = string.Format("{0}{1}", bundlePath, GetBundleHashForFiles(filePaths));
    
            var bundleIsRegistered = BundleTable
                .Bundles
                .GetRegisteredBundles()
                .Where(bundle => bundle.Path == bundlePath)
                .Any();
    
            if(!bundleIsRegistered)
            {
                var bundle = new StyleBundle(bundlePath);
                bundle.Include(filePaths);
                BundleTable.Bundles.Add(bundle);
            }
    
            return Styles.Render(bundlePath);
        }
    
        static string GetBundleHashForFiles(IEnumerable<string> filePaths)
        {
            // Create a unique hash for this set of files
            var aggregatedPaths = filePaths.Aggregate((pathString, next) => pathString + next);
            var Md5 = MD5.Create();
            var encodedPaths = Encoding.UTF8.GetBytes(aggregatedPaths);
            var hash = Md5.ComputeHash(encodedPaths);
            var bundlePath = hash.Aggregate(string.Empty, (hashString, next) => string.Format("{0}{1:x2}", hashString, next));
            return bundlePath;
        }
    
        5
  •  3
  •   j0k gauthamp    13 年前

    你有没有试过从( 样式绑定 脚本捆绑包 ),在构造函数中不添加任何包含,然后重写

    public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)
    

    我这样做是为了动态样式表,每次请求都会调用EnumerateFiles。这可能不是最好的解决方案,但它确实有效。

        6
  •  0
  •   SY6Dave    8 年前

    很抱歉恢复了一个死线程,但我在Umbraco网站上遇到了类似的Bundle缓存问题,我希望当用户在后台更改漂亮的版本时,样式表/脚本能够自动缩小。

    我已经拥有的代码是(在样式表的onSaved方法中):

     BundleTable.Bundles.Add(new StyleBundle("~/bundles/styles.min.css").Include(
                               "~/css/main.css"
                            ));
    

    和(在ApplicationStarted上):

    BundleTable.EnableOptimizations = true;
    

    无论我尝试了什么,“~/bundles/styles.min.css”文件似乎都没有更改。在页面的头部,我最初是这样加载样式表的:

    <link rel="stylesheet" href="~/bundles/styles.min.css" />
    

    然而,我通过将其更改为:

    @Styles.Render("~/bundles/styles.min.css")
    

    Render方法在文件名的末尾拉入一个查询字符串,我猜这就是上面Hao描述的缓存键。

    对我来说,事情就这么简单。希望这能帮助到像我这样在谷歌上搜索了几个小时,却只能找到几年前的帖子的人!