Как заставить BundleCollection очистить кэшированные пакеты сценариев в MVC4



... или как я научился перестать беспокоиться и просто писать код против полностью недокументированных API от Microsoft. Есть ли какая-либо фактическая документация официального 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 это то же самое, что и до того, как я удалил и воссоздал пакет. Под "тем же" я подразумеваю, что хэш содержимого не изменяется, чтобы отразить новое содержимое пакета.

edit... на самом деле, все гораздо хуже. Элемент пакета кэшируется как-то за пределами Bundles коллекция. Если я просто создам свой собственный случайный хэш, чтобы браузер не кэшировал скрипт, ASP.NET возвращает старый скрипт. Так что, по-видимому, удаление пучка из BundleTable.Bundles на самом деле ничего не делает.

Я могу просто изменить псевдоним, чтобы обойти эту проблему, и это нормально для разработки, но мне не нравится эта идея, так как это означает, что либо я должен устаревать псевдонимы после каждой загрузки страницы, либо иметь BundleCollection, который растет в размере на каждой странице нагрузка. Если вы оставите это в рабочей среде, это будет катастрофой.

поэтому кажется, что когда скрипт подается, он кэшируется независимо от фактического

159   6  

6 ответов:

мы слышим вашу боль в документации, к сожалению, эта функция все еще меняется довольно быстро, и генерация документации имеет некоторое отставание и может быть устаревшей почти сразу. сообщение в блоге Рика в актуальном состоянии, и я попытался ответить на вопросы здесь, а также распространять текущую информацию в то же время. В настоящее время мы находимся в процессе создания нашего официального сайта codeplex, который всегда будет иметь актуальную документацию.

теперь в отношении вашего конкретного вопроса о том, как очистить пакеты от кэша.

  1. мы храним в комплекте ответ внутри ASP.NET кэширование с использованием ключа, сгенерированного из запрошенного url-адреса пакета, т. е. Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"] мы также устанавливаем зависимости кэша для всех файлов и каталогов, которые использовались для создания этого пакета. Поэтому, если какой-либо из базовых файлов или каталогов изменится, запись в кэше будет сброшена.

  2. мы действительно не поддерживаем живое обновление BundleTable/BundleCollection на основе каждого запроса. Полностью поддерживаемый сценарий заключается в том, что пакеты настраиваются во время запуска приложения(это значит, что все работает правильно в сценарии веб-фермы, иначе некоторые запросы пакетов будут 404, если они будут отправлены на неправильный сервер). Глядя на ваш пример кода, я предполагаю, что вы пытаетесь динамически изменять коллекцию bundle по конкретному запросу? Любой вид администрирования/реконфигурации пакета должен сопровождаться домен сброса, чтобы гарантировать все было настроено правильно.

поэтому избегайте изменения определений пакетов без повторного использования домена приложения. Вы можете изменять фактические файлы внутри своих пакетов, которые должны автоматически обнаруживаться и генерировать новые хэш-коды для ваших URL-адресов пакетов.

у меня аналогичная проблема.
В моем классе 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

@Scripts.Url дает мне "оптимизировать" путь пакета в то время как @Scripts.Render генерирует правильный.
То же самое происходит с BundleTable.Bundles.ResolveBundleUrl.

я использую Visual Studio 2010 + MVC 4 + Framework .Net 4.0.

принимая во внимание рекомендации Хао кунга не делать этого из-за сценариев веб-фермы, я думаю, что есть много сценариев, где вы можете это сделать. Вот решение:

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

Я также столкнулся с проблемами с обновлением пакетов без перестройки. Вот важные вещи, чтобы понять:

  • пакет не обновляется при изменении путей к файлам.
  • пакет обновляется, если виртуальный путь пакета изменяется.
  • пакет обновляется, если файлы на диске меняются.

Итак, зная, что, если вы делаете динамическое связывание, вы можете написать некоторый код, чтобы сделать виртуальный путь пакета на основе путей к файлам. Я рекомендую хэшировать пути к файлам и добавлять этот хэш в конец виртуального пути пакета. Таким образом, когда пути к файлам изменяются, виртуальный путь и пакет будут обновлены.

вот код, который я закончил с тем, что решил проблему для меня:

    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)

Я делаю это для динамических таблиц стилей и EnumerateFiles вызывается при каждом запросе. Это, вероятно, не самое лучшее решение, но оно работает.

извиняюсь, чтобы оживить мертвый поток, однако я столкнулся с аналогичной проблемой с кэшированием пакетов на сайте Umbraco, где я хотел, чтобы таблицы стилей/скрипты автоматически уменьшались, когда пользователь изменял красивую версию в бэкэнде.

код, который у меня уже был (в методе onSaved для таблицы стилей):

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

и (onApplicationStarted):

BundleTable.EnableOptimizations = true;

независимо от того, что я пробовал, "~/bundles/styles.минута.css " файл, похоже, не изменился. В глава моей страницы, я изначально загружал в таблицу стилей так:

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

однако, я получил его на работу, изменив это:

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

Стили.Метод Render извлекает строку запроса в конце имени файла, который, как я предполагаю, является ключом кэша, описанным Hao выше.

для меня это было так просто. Надеюсь, это поможет кому-нибудь еще, как я, кто гуглил это в течение нескольких часов и мог найти только несколько летних сообщений!

    Ничего не найдено.

Добавить ответ:
Отменить.