87

... 或者我是如何学会停止担心的,而只是针对来自 Microsoft 的完全未记录的 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,即使您在重用它之前删除了它引用的包,它也会响应缓存中的任何内容,并且更改Bundles对象不会刷新缓存 - 因此只有项目(或相反,具有不同名称的新项目)将永远被使用。

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

知道我会怎么做吗?

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

4

6 回答 6

34

我们听到您对文档的痛苦,不幸的是,此功能仍在快速变化,并且生成文档有一些滞后,并且几乎可以立即过时。 Rick 的博客文章是最新的,我也尝试在这里回答问题,同时传播当前信息。我们目前正在建立我们的官方 codeplex 站点,该站点将始终具有最新文档。

现在关于如何从缓存中刷新包的具体问题。

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

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

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

于 2012-09-07T20:24:18.337 回答
21

我有一个类似的问题。
在我的课堂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似乎在解决捆绑路径时遇到了问题。

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

图 1

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

图 2

正文中包含相同的捆绑包。

图 3

@Scripts.Url@Scripts.Render在生成正确路径的同时给我捆绑包的“优化”路径。
同样的事情发生在BundleTable.Bundles.ResolveBundleUrl.

我正在使用 Visual Studio 2010 + MVC 4 + Framework .Net 4.0。

于 2012-09-07T16:29:15.327 回答
8

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

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

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

@Scripts.Render("~/bundles/your-bundle-virtual-path")
于 2014-11-20T13:05:00.103 回答
4

我还遇到了更新捆绑包而不重建的问题。以下是需要了解的重要事项:

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

所以知道了,如果你正在做动态捆绑,你可以编写一些代码来使捆绑的虚拟路径基于文件路径。我建议散列文件路径并将该散列附加到包的虚拟路径的末尾。这样,当文件路径更改时,虚拟路径也会更改,并且捆绑包将更新。

这是我最终得到的代码,它为我解决了这个问题:

    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;
    }
于 2015-11-04T16:54:17.003 回答
3

您是否尝试过从(StyleBundleScriptBundle)派生,在构造函数中不添加任何内容,然后覆盖

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

我为动态样式表执行此操作,并且 EnumerateFiles 在每个请求上都会被调用。这可能不是最好的解决方案,但它确实有效。

于 2012-11-13T16:45:43.213 回答
0

很抱歉恢复死线程,但是我在 Umbraco 站点中遇到了与 Bundle 缓存类似的问题,我希望样式表/脚本在用户更改后端漂亮版本时自动缩小。

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

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

和(onApplicationStarted):

BundleTable.EnableOptimizations = true;

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

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

但是,我通过将其更改为:

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

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

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

于 2018-02-06T10:26:25.027 回答