如何强制BundleCollection刷新MVC4中的caching脚本包
…或者我学会了如何停止担心,并且仅仅针对微软公司完全没有logging的API编写代码 。 有没有官方的System.Web.Optimization
版本的实际文档? “因为我当然找不到任何东西,没有XML文档,所有的博客post都提到了RC API,它们有很大的不同。 安美居..
我正在写一些代码来自动解决JavaScript的依赖关系,并从这些依赖关系即时创build捆绑。 除非您编辑脚本或以其他方式进行影响软件包而不重新启动应用程序的更改,否则所有更改都不会发生变化。 所以我添加了一个选项来禁用caching的依赖关系在开发中使用。
但是, 即使Bundle集合已经改变 ,显然BundleTables
cachingURL。 例如,在我自己的代码中,当我想重新创build一个包时,我会这样做:
// 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"
每当我删除并重新创build一个具有相同别名的包时 ,绝对不会发生任何事情:从bundleUrl
返回的ResolveBundleUrl
与之前删除&重新创build包相同。 “相同”我的意思是内容哈希值不变,以反映包的新内容。
编辑 …实际上,比这更糟糕。 bundle本身被caching在Bundles
集合之外。 如果我只是生成我自己的随机哈希以防止浏览器caching脚本,ASP.NET将返回旧脚本 。 所以,显然,从BundleTable.Bundles
中删除一个包实际上并没有做任何事情。
我可以简单地改变别名来解决这个问题,这对于开发是可以的,但是我不喜欢这个想法,因为这意味着要么在每次页面加载之后弃用别名,要么在BundleCollection的大小上增长每页加载。 如果你把它放在生产环境中,这将是一场灾难。
所以看起来,当一个脚本被提供时,它被cachingBundleTables.Bundles
依赖于实际的BundleTables.Bundles
对象。 因此,如果重新使用URL,即使在重用之前已经移除了引用的bundle,它也会响应caching中的任何内容,并且更改Bundles
对象不会刷新caching – 所以只有新项目或者更确切地说,使用不同名称的新项目)。
行为似乎很奇怪…从集合中删除的东西应该从caching中删除它。 但事实并非如此。 必须有一种方法来刷新此caching,并使用BundleCollection
的当前内容而不是第一次访问该caching时caching的内容。
任何想法我会如何做到这一点?
有这个ResetAll
方法有一个未知的目的,但它只是打破了事情,所以不是这样。
我们听到你对文档的痛苦,不幸的是,这个特性仍然在变化很快,而且生成的文档有一些滞后,并且几乎可以立即过时。 里克的博客文章是最新的,我也尝试回答这里的问题,同时传播当前的信息。 目前,我们正在build立我们的官方codeplex网站,将始终有最新的文件。
现在关于如何从caching中刷新bundle的具体问题。
-
我们使用从请求的bundle url生成的密钥(即
Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]
caching依赖关系存储在ASP.NETcaching中所有用来生成这个包的文件和目录。 因此,如果任何底层文件或目录发生更改,caching条目将被刷新。 -
我们并不真正支持每个请求的实时更新BundleTable / BundleCollection。 完全支持的scheme是在应用程序启动期间configuration了bundle(这是为了在web场景中正常工作,否则如果发送到错误的服务器,一些bundle请求最终会变成404)。 看看你的代码示例,我的猜测是,你试图在一个特定的请求上dynamic修改bundle集合? 任何types的软件包pipe理/重新configuration都应附带一个应用程序域重置,以确保所有设置都正确。
所以要避免修改你的bundle定义而不回收你的应用程序域。 您可以自由修改捆绑包内的实际文件,这些文件应该被自动检测并为您的捆绑url生成新的哈希码。
我也有类似的问题。
在我的BundleConfig
类中,我试图看看使用BundleTable.EnableOptimizations = true
的效果。
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { BundleTable.EnableOptimizations = true; bundles.Add(...); } }
一切工作正常。
在某个时候,我正在做一些debugging,并将属性设置为false。
我努力了解发生了什么事情,因为看起来jquery(第一个)的包不会被parsing和加载( /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
似乎有问题来解决束path。
为了简单起见,我添加了一些图片:
我已经closures了优化,并捆绑了几个脚本。
正文中包含相同的包。
@Scripts.Url
给了我捆绑的“优化”path,而@Scripts.Render
生成正确的path。
BundleTable.Bundles.ResolveBundleUrl
发生同样的情况。
我正在使用Visual Studio 2010 + MVC 4 + Framework .Net 4.0。
考虑到浩弘的build议,不要因为网站的情况下这样做,我认为有很多情况下,你可能想要做到这一点。 这是一个解决scheme:
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时都有效 – 换句话说,这将在debugging或生活场景中抛出正确的标记,其中:
@Scripts.Render("~/bundles/your-bundle-virtual-path")
我也碰到了更新包而不重build的问题。 以下是要了解的重要事项:
- 如果文件path改变,该软件包不会被更新。
- 如果软件包的虚拟path发生变化,软件包将会更新。
- 如果磁盘上的文件发生更改,该软件包将会更新。
因此,要知道,如果你正在做dynamic绑定,你可以编写一些代码来使得bundle的虚拟path基于文件path。 我build议对文件path进行散列并将散列附加到该包的虚拟path的末尾。 这样当文件path发生变化时,虚拟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; }
你尝试派生( StyleBundle或ScriptBundle ),在你的构造函数中添加没有内含物,然后重写
public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)
我这样做的dynamic样式表和EnumerateFiles得到每个请求调用。 这可能不是最好的解决scheme,但它的工作原理。