C# 如何强制BundleCollection刷新MVC4中的缓存脚本包

C# 如何强制BundleCollection刷新MVC4中的缓存脚本包,c#,asp.net,asp.net-mvc-4,asp.net-optimization,bundling-and-minification,C#,Asp.net,Asp.net Mvc 4,Asp.net Optimization,Bundling And Minification,。。。或者我是如何学会不再担心,只是针对微软完全没有文档的API编写代码的。是否有官方System.Web.Optimizationrelease的实际文档因为我肯定找不到任何文档,没有XML文档,所有的博客帖子都引用了RC API,这与以前有很大不同。随便 我正在编写一些代码来自动解析javascript依赖项,并正在从这些依赖项动态创建捆绑包。一切都很好,除非您编辑脚本或在不重新启动应用程序的情况下进行可能会影响捆绑包的更改,否则这些更改不会得到反映。所以我添加了一个选项来禁用依赖项的缓存,

。。。或者我是如何学会不再担心,只是针对微软完全没有文档的API编写代码的。是否有官方
System.Web.Optimization
release的实际文档因为我肯定找不到任何文档,没有XML文档,所有的博客帖子都引用了RC API,这与以前有很大不同。随便

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

但是,显然,
BundleTables
会缓存URL,即使bundle集合已更改。例如,在我自己的代码中,当我想要重新创建捆绑包时,我会执行以下操作:

// 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"
每当我删除并重新创建具有相同别名的捆绑包时,绝对不会发生任何事情:从
ResolveBundleUrl
返回的
bundleUrl
与我删除并重新创建捆绑包之前相同。我所说的“相同”是指内容哈希保持不变,以反映捆绑包的新内容

编辑。。。事实上,比这更糟糕。捆绑包本身以某种方式缓存在
Bundles
集合之外。如果我只是生成自己的随机散列以防止浏览器缓存脚本,ASP.NET将返回旧脚本。因此,显然,从
BundleTable.Bundles
中删除一个bundle实际上没有任何作用

我可以简单地更改别名来解决这个问题,这对于开发来说是可以的,但我不喜欢这个想法,因为这意味着我必须在每次页面加载后弃用别名,或者在每次页面加载时都有一个BundleCollection,它的大小会不断增加。如果在生产环境中保持此状态,那将是一场灾难

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

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

你知道我该怎么做吗


有一个
ResetAll
方法,它有一个未知的目的,但它只是破坏了一些东西,所以这不是它。

我有一个类似的问题。
在我的课程
BundleConfig
中,我试图了解使用
BundleTable.EnableOptimizations=true
的效果

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

        bundles.Add(...);
    }
}
一切正常。
在某个时候,我正在进行一些调试,并将属性设置为false。
我很难理解发生了什么,因为jquery的bundle(第一个)似乎无法解析和加载(
/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
在解析捆绑路径时似乎存在问题

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

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

相同的束包含在主体中

@Scripts.Url
提供捆绑包的“优化”路径,而
@Scripts.Render
生成正确的路径。
BundleTable.Bundles.ResolveBundleUrl
也会发生同样的情况


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

我们听到您对文档的抱怨,不幸的是,此功能仍在快速变化,生成文档有一定的滞后性,几乎可以立即过时。是最新的,我在这里尝试回答问题,同时传播最新信息。我们目前正在建立我们的官方codeplex网站,该网站将始终具有最新的文档

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

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

  • 我们并不真正支持根据每个请求实时更新BundleTable/BundleCollection。完全受支持的方案是在应用程序启动期间配置捆绑包(这是为了在web场方案中一切正常工作,否则一些捆绑包请求如果发送到错误的服务器,最终将成为404)。看看您的代码示例,我猜您正在尝试根据特定请求动态修改bundle集合?任何类型的捆绑包管理/重新配置都应伴随appdomain重置,以确保所有内容都已正确设置

  • 因此,避免修改您的包
    public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)
    
    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);
    
    @Scripts.Render("~/bundles/your-bundle-virtual-path")
    
        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;
        }
    
     BundleTable.Bundles.Add(new StyleBundle("~/bundles/styles.min.css").Include(
                               "~/css/main.css"
                            ));
    
    BundleTable.EnableOptimizations = true;
    
    <link rel="stylesheet" href="~/bundles/styles.min.css" />
    
    @Styles.Render("~/bundles/styles.min.css")