web-dev-qa-db-ja.com

MVC4でBundleCollectionにキャッシュされたスクリプトバンドルをフラッシュさせる方法

...またはMicrosoftの完全に文書化されていないAPIに対して心配するのをやめてコードを書くことを学んだ方法。公式の実際の文書はありますかSystem.Web.Optimizationリリース? 「私は確かに見つけることができません。XMLドキュメントはありません。すべてのブログ投稿は、実質的に異なるRC APIを参照しています。 Anyhoo ..

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オブジェクトを変更してもキャッシュはフラッシュされません-そのため、newアイテム(または、別の名前の新しいアイテム)のみが使用されます。

動作は奇妙に思えます...コレクションから何かを削除すると、キャッシュからも削除されます。しかし、そうではありません。このキャッシュをフラッシュし、そのバンドルが最初にアクセスされたときにキャッシュしたものではなく、BundleCollectionの現在の内容を使用する方法が必要です。

私はこれをどのように行うのでしょうか?

未知の目的を持つこのResetAllメソッドがありますが、とにかく物事を壊しているだけです。

83
Jamie Treworgy

残念ながら、この機能は非常に急速に変化しており、ドキュメントの生成には多少の遅れがあり、ほぼすぐに時代遅れになる可能性があります。 Rickのブログ投稿 は最新のものであり、その間に現在の情報を広めるために、ここでも質問に答えようとしました。私たちは現在、常に最新のドキュメントがある公式codeplexサイトのセットアップを進めています。

次に、キャッシュからバンドルをフラッシュする方法の特定の問題について説明します。

  1. 要求されたバンドルURLから生成されたキーを使用して、ASP.NETキャッシュ内にバンドルされた応答を格納します。つまり、Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]また、このバンドルを生成するために使用されたすべてのファイルとディレクトリに対するキャッシュの依存関係を設定します。したがって、基になるファイルまたはディレクトリのいずれかが変更されると、キャッシュエントリがフラッシュされます。

  2. リクエストごとにBundleTable/BundleCollectionのライブ更新を実際にサポートしていません。完全にサポートされているシナリオは、アプリの起動時にバンドルが設定されることです(これにより、Webファームシナリオですべてが適切に機能します。あなたのコード例を見ると、特定のリクエストでバンドルコレクションを動的に変更しようとしているのでしょうか?あらゆる種類のバンドル管理/再構成には、すべてが正しくセットアップされたことを保証するために、appdomainのリセットを伴う必要があります。

そのため、アプリドメインをリサイクルせずにバンドル定義を変更しないでください。バンドル内の実際のファイルを自由に変更できます。ファイルは自動的に検出され、バンドルURLの新しいハッシュコードが生成されます。

33
Hao Kung

同様の問題があります。
クラス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プロパティを変更する場合にのみ、これら2つのメソッドを実行する必要があることに気付きました。

更新:

さらに掘り下げてみると、_BundleTable.Bundles.ResolveBundleUrl_と_@Scripts.Url_にバンドルパスを解決する問題があるようです。

簡単にするために、いくつかの画像を追加しました。

image 1

最適化をオフにして、いくつかのスクリプトをバンドルしました。

image 2

同じバンドルが本体に含まれています。

image 3

_@Scripts.Url_はバンドルの「最適化された」パスを提供し、_@Scripts.Render_は適切なパスを生成します。
_BundleTable.Bundles.ResolveBundleUrl_でも同じことが起こります。

私はVisual Studio 2010 + MVC 4 + Framework .Net 4.0を使用しています

20
LeftyX

Webファームのシナリオのためにこれを行わないという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")
8
Zac

また、再構築せずにバンドルを更新すると問題が発生しました。理解すべき重要な事項は次のとおりです。

  • ファイルパスが変更されても、バンドルは更新されません。
  • バンドルの仮想パスが変更されると、バンドルは更新されます。
  • ディスク上のファイルが変更されると、バンドルは更新されます。

そのため、動的バンドリングを行っている場合、バンドルの仮想パスをファイルパスに基づいて作成するコードを作成できます。ファイルパスをハッシュし、そのハッシュをバンドルの仮想パスの最後に追加することをお勧めします。この方法では、ファイルパスが変更されると仮想パスも変更され、バンドルが更新されます。

私がこの問題を解決したコードは次のとおりです。

    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;
    }
4
FriendScottN

StyleBundleまたはScriptBundle)から派生させ、コンストラクターにインクルードを追加せずにオーバーライドしてみましたか?

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

これは動的スタイルシートに対して行い、EnumerateFilesはすべてのリクエストで呼び出されます。これはおそらく最大の解決策ではありませんが、機能します。

3
tulde23

デッドスレッドを復活させることをおologiesびしますが、Umbracoサイトでバンドルキャッシュに関する同様の問題が発生し、ユーザーがバックエンドできれいなバージョンを変更したときにスタイルシート/スクリプトが自動的に縮小されるようにしました。

私がすでに持っていたコードは(スタイルシートの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が説明したキャッシュキーであると推測しているファイル名の最後にクエリ文字列を取り込みます。

私にとっては、それはそれと同じくらい簡単でした。これが何時間もこれをグーグルしていて、数年前の投稿しか見つけられない私のような誰かを助けることを願っています!

0
SY6Dave