... или как я научился не беспокоиться и просто писать код для полностью недокументированных API от Microsoft . Есть ли актуальная документация официального 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
объекта не очищает кеш, поэтому только новые элементы (или скорее, новые предметы с другим названием) будут когда-либо использоваться.
Поведение кажется странным ... удаление чего-либо из коллекции должно удалить это из кеша. Но это не так. Должен быть способ очистить этот кеш и заставить его использовать текущее содержимое BundleCollection
вместо того, что было кэшировано при первом доступе к этому пакету.
Есть идеи, как бы я это сделал?
Есть ResetAll
метод, цель которого неизвестна, но он все равно ломает вещи, так что это не так.
источник
Ответы:
Мы слышим, что вы беспокоитесь о документации, к сожалению, эта функция все еще довольно быстро меняется, а создание документации имеет некоторое отставание и может устареть почти сразу. Сообщение в блоге Рика обновлено, и я попытался ответить на вопросы здесь, чтобы тем временем распространять текущую информацию. В настоящее время мы находимся в процессе настройки нашего официального сайта codeplex, на котором всегда будет актуальная документация.
Теперь что касается вашей конкретной проблемы, связанной с очисткой пакетов из кеша.
Мы сохраняем связанный ответ внутри кеша ASP.NET, используя ключ, сгенерированный на основе запрошенного URL-адреса пакета, т.е.
Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]
мы также устанавливаем зависимости кеша для всех файлов и каталогов, которые использовались для создания этого пакета. Поэтому, если какой-либо из базовых файлов или каталогов изменится, запись кеша будет очищена.На самом деле мы не поддерживаем обновление BundleTable / BundleCollection в реальном времени для каждого запроса. Полностью поддерживаемый сценарий заключается в том, что пакеты настраиваются во время запуска приложения (это значит, что в сценарии веб-фермы все работает правильно, в противном случае некоторые запросы пакетов в конечном итоге будут 404, если будут отправлены на неправильный сервер). Глядя на ваш пример кода, я предполагаю, что вы пытаетесь динамически изменять коллекцию пакетов по определенному запросу? Любое администрирование / реконфигурация пакета должно сопровождаться сбросом домена приложения, чтобы гарантировать, что все настроено правильно.
Поэтому избегайте изменения определений пакетов без повторного использования домена приложения. Вы можете изменять фактические файлы внутри ваших пакетов, которые должны автоматически обнаруживаться и генерировать новые хэш-коды для 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
похоже, у меня проблемы с разрешением пути к пакету.Для простоты я добавил несколько изображений:
Я отключил оптимизацию и собрал несколько скриптов.
Такой же комплект входит в корпус.
@Scripts.Url
дает мне "оптимизированный" путь пакета, в то время как@Scripts.Render
генерирует правильный.То же самое происходит с
BundleTable.Bundles.ResolveBundleUrl
.Я использую Visual Studio 2010 + MVC 4 + Framework .Net 4.0.
источник
ResetAll
и пробовал установитьEnableOptimizations
значение false как при запуске, так и при встроенном, когда мне нужно сбросить кеш, ничего не происходит. Ага.Принимая во внимание рекомендации Хао Гун не делать этого из-за сценариев веб-фермы, я думаю, что существует множество сценариев, в которых вы можете захотеть это сделать. Вот решение:
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")
источник
GenerateBundleResponse
Я также столкнулся с проблемами при обновлении пакетов без перекомпоновки. Вот что важно понять:
Итак, зная, что, если вы выполняете динамическое объединение, вы можете написать код, чтобы виртуальный путь пакета был основан на путях к файлам. Я рекомендую хешировать пути к файлам и добавлять этот хеш в конец виртуального пути пакета. Таким образом, при изменении путей к файлам изменится и виртуальный путь, и пакет обновится.
Вот код, который я получил, решив проблему для меня:
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; }
источник
Aggregate
конкатенации строк из-за риска того, что кто-то не подумает о многократно используемом алгоритме Шлемиля-художника+
. Вместо этого просто делайstring.Join("", filePaths)
. У этого не будет этой проблемы даже для очень больших входов.Вы пытались получить из ( StyleBundle или ScriptBundle ), не добавляя включений в свой конструктор, а затем переопределяя
public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)
Я делаю это для динамических таблиц стилей, и EnumerateFiles вызывается при каждом запросе. Возможно, это не лучшее решение, но оно работает.
источник
Приносим извинения за возрождение мертвого потока, однако я столкнулся с аналогичной проблемой с кешированием Bundle на сайте 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 вытягивает строку запроса в конце имени файла, которое, как я предполагаю, является ключом кеша, описанным Хао выше.
Для меня это было так просто. Надеюсь, это поможет любому, как я, кто часами гуглил и мог найти только сообщения нескольких лет назад!
источник