... lub jak nauczyłem się przestać się martwić i po prostu pisać kod w całkowicie nieudokumentowanych interfejsach API firmy Microsoft . Czy istnieje rzeczywista dokumentacja oficjalnego System.Web.Optimization
wydania? Ponieważ na pewno nie mogę znaleźć żadnego, nie ma dokumentów XML, a wszystkie posty na blogu odnoszą się do RC API, które jest zasadniczo inne. Anyhoo ..
Piszę kod, aby automatycznie rozwiązywać zależności javascript i tworzę pakiety w locie z tych zależności. Wszystko działa świetnie, z wyjątkiem sytuacji, gdy edytujesz skrypty lub w inny sposób wprowadzasz zmiany, które wpłynęłyby na pakiet bez ponownego uruchamiania aplikacji, zmiany nie zostaną odzwierciedlone. Dodałem więc opcję wyłączenia buforowania zależności do wykorzystania w rozwoju.
Jednak najwyraźniej BundleTables
buforuje adres URL, nawet jeśli kolekcja paczek uległa zmianie . Na przykład w moim własnym kodzie, gdy chcę ponownie utworzyć pakiet, robię coś takiego:
// 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"
Za każdym razem, gdy usuwam i ponownie tworzę pakiet z tym samym aliasem , absolutnie nic się nie dzieje: bundleUrl
zwracany plik ResolveBundleUrl
jest taki sam, jak przed usunięciem i odtworzeniem pakietu. Przez „ten sam” rozumiem, że skrót zawartości pozostaje niezmieniony, aby odzwierciedlić nową zawartość pakietu.
edytuj ... właściwie, jest znacznie gorzej. Sam pakiet jest w jakiś sposób buforowany poza Bundles
kolekcją. Jeśli po prostu wygeneruję własny losowy skrót, aby zapobiec buforowaniu skryptu przez przeglądarkę, ASP.NET zwraca stary skrypt . Najwyraźniej usunięcie pakietu z BundleTable.Bundles
faktycznie nic nie robi.
Mogę po prostu zmienić alias, aby obejść ten problem, i jest to w porządku w przypadku programowania, ale nie podoba mi się ten pomysł, ponieważ oznacza to, że muszę wycofywać aliasy po każdym załadowaniu strony lub mieć kolekcję BundleCollection, która rośnie w każde ładowanie strony. Jeśli zostawisz to włączone w środowisku produkcyjnym, byłaby to katastrofa.
Wygląda więc na to, że kiedy skrypt jest obsługiwany, jest buforowany niezależnie od rzeczywistego BundleTables.Bundles
obiektu. Więc jeśli ponownie użyjesz adresu URL, nawet jeśli usunąłeś pakiet, do którego się odnosił przed ponownym użyciem, odpowiada on tym, co jest w jego pamięci podręcznej, a zmiana Bundles
obiektu nie powoduje opróżnienia pamięci podręcznej - więc tylko nowe elementy (lub raczej nowe przedmioty o innej nazwie) byłyby kiedykolwiek używane.
Zachowanie wydaje się dziwne ... usunięcie czegoś z kolekcji powinno usunąć to z pamięci podręcznej. Ale tak nie jest. Musi istnieć sposób, aby opróżnić tę pamięć podręczną i użyć bieżącej zawartości plikuBundleCollection
zamiast tego, co buforowała, gdy po raz pierwszy uzyskano dostęp do tego pakietu.
Masz jakiś pomysł, jak bym to zrobił?
Jest taka ResetAll
metoda, która ma nieznane przeznaczenie, ale i tak wszystko psuje, więc nie o to chodzi.
źródło
Odpowiedzi:
Słyszymy, że cierpisz z powodu dokumentacji, niestety ta funkcja wciąż się zmienia dość szybko, a generowanie dokumentacji ma pewne opóźnienie i może być nieaktualne prawie natychmiast. Wpis na blogu Ricka jest aktualny. W międzyczasie próbowałem odpowiedzieć na pytania, aby rozpowszechniać aktualne informacje. Obecnie jesteśmy w trakcie tworzenia naszej oficjalnej witryny codeplex, która będzie zawsze mieć aktualną dokumentację.
Teraz w odniesieniu do twojego konkretnego problemu, jak opróżniać pakiety z pamięci podręcznej.
Przechowujemy odpowiedź w pakiecie w pamięci podręcznej ASP.NET przy użyciu klucza wygenerowanego z żądanego adresu URL pakietu, tj.
Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]
Konfigurujemy również zależności pamięci podręcznej dla wszystkich plików i katalogów, które zostały użyte do wygenerowania tego pakietu. Więc jeśli którykolwiek z podstawowych plików lub katalogów ulegnie zmianie, wpis pamięci podręcznej zostanie opróżniony.Naprawdę nie obsługujemy aktualizacji na żywo BundleTable / BundleCollection na podstawie żądania. W pełni obsługiwany scenariusz polega na tym, że pakiety są konfigurowane podczas uruchamiania aplikacji (tak więc wszystko działa poprawnie w scenariuszu farmy internetowej, w przeciwnym razie niektóre żądania pakietów zakończyłyby się 404, jeśli zostałyby wysłane na niewłaściwy serwer). Patrząc na Twój przykład kodu, domyślam się, że próbujesz dynamicznie modyfikować kolekcję pakietów na określone żądanie? Każdemu rodzajowi administracji / rekonfiguracji pakietu powinien towarzyszyć reset domeny aplikacji, aby zagwarantować, że wszystko zostało poprawnie skonfigurowane.
Dlatego unikaj modyfikowania definicji pakietów bez recyklingu domeny aplikacji. Możesz modyfikować rzeczywiste pliki w swoich paczkach, które powinny być automatycznie wykrywane i generować nowe hashcody dla adresów URL pakietów.
źródło
Mam podobny problem.
Na zajęciach
BundleConfig
starałem się sprawdzić, jaki jest efekt używaniaBundleTable.EnableOptimizations = true
.public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { BundleTable.EnableOptimizations = true; bundles.Add(...); } }
Wszystko działało dobrze.
W pewnym momencie robiłem debugowanie i ustawiłem właściwość na false.
Starałem się zrozumieć, co się dzieje, ponieważ wydawało się, że pakiet jquery (pierwszy) nie zostanie rozwiązany i załadowany (
/bundles/jquery?v=
).Po kilku przekleństwach myślę, że (?!) udało mi się wszystko uporządkować. Spróbuj dodać
bundles.Clear()
ibundles.ResetAll()
na początku rejestracji i wszystko powinno znowu zacząć działać.public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Clear(); bundles.ResetAll(); BundleTable.EnableOptimizations = false; bundles.Add(...); } }
Zdałem sobie sprawę, że muszę uruchomić te dwie metody tylko wtedy, gdy zmienię
EnableOptimizations
właściwość.AKTUALIZACJA:
Kopiąc głębiej, odkryłem to
BundleTable.Bundles.ResolveBundleUrl
i@Scripts.Url
wydaje mi się, że mam problemy z rozwiązaniem ścieżki pakietu.Dla uproszczenia dodałem kilka zdjęć:
Wyłączyłem optymalizację i dołączyłem kilka skryptów.
Ten sam pakiet znajduje się w korpusie.
@Scripts.Url
daje mi „zoptymalizowaną” ścieżkę pakietu, podczas gdy@Scripts.Render
generuje właściwą.To samo dzieje się z
BundleTable.Bundles.ResolveBundleUrl
.Używam Visual Studio 2010 + MVC 4 + Framework .Net 4.0.
źródło
ResetAll
i próbowałem ustawić wartośćEnableOptimizations
false zarówno podczas uruchamiania, jak i w linii, gdy muszę zresetować pamięć podręczną, nic się nie dzieje. Argh.Mając na uwadze zalecenia Hao Kunga, aby tego nie robić ze względu na scenariusze farm internetowych, myślę, że istnieje wiele scenariuszy, w których możesz to zrobić. Oto rozwiązanie:
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);
Możesz zadzwonić na powyższy kod w dowolnym momencie, a Twoje pakiety zostaną zaktualizowane. Działa to zarówno wtedy, gdy EnableOptimizations ma wartość true lub false - innymi słowy, spowoduje to wyrzucenie prawidłowego znacznika w scenariuszach debugowania lub na żywo, z:
@Scripts.Render("~/bundles/your-bundle-virtual-path")
źródło
GenerateBundleResponse
Napotkałem również problemy z aktualizacją pakietów bez przebudowy. Oto ważne rzeczy do zrozumienia:
Wiedząc o tym, jeśli robisz dynamiczne tworzenie pakietów, możesz napisać kod, aby wirtualna ścieżka pakietu była oparta na ścieżkach plików. Zalecam haszowanie ścieżek plików i dołączanie tego skrótu na końcu ścieżki wirtualnej pakietu. W ten sposób, gdy zmieniają się ścieżki plików, zmienia się również ścieżka wirtualna i pakiet zostanie zaktualizowany.
Oto kod, który otrzymałem, a który rozwiązał problem:
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; }
źródło
Aggregate
konkatenacji ciągów ze względu na ryzyko, że ktoś nie pomyśli o nieodłącznym algorytmie Schlemiel the Painter w wielokrotnym użyciu+
. Zamiast tego po prostu zróbstring.Join("", filePaths)
. To nie będzie miało tego problemu, nawet w przypadku bardzo dużych nakładów.Czy próbowałeś wyprowadzić z ( StyleBundle lub ScriptBundle ), nie dodając żadnych inkluzji w konstruktorze, a następnie przesłaniając
public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)
Robię to dla dynamicznych arkuszy stylów, a EnumerateFiles jest wywoływana na każde żądanie. To chyba nie najlepsze rozwiązanie, ale działa.
źródło
Przepraszamy za wskrzeszenie martwego wątku, jednak napotkałem podobny problem z buforowaniem pakietów na stronie Umbraco, gdzie chciałem, aby arkusze stylów / skrypty były automatycznie zmniejszane, gdy użytkownik zmienił ładną wersję w zapleczu.
Kod, który już miałem (w metodzie onSaved dla arkusza stylów):
BundleTable.Bundles.Add(new StyleBundle("~/bundles/styles.min.css").Include( "~/css/main.css" ));
i (onApplicationStarted):
BundleTable.EnableOptimizations = true;
Bez względu na to, co próbowałem, plik „~ / bundles / styles.min.css” nie zmienił się. W nagłówku mojej strony pierwotnie ładowałem arkusz stylów w następujący sposób:
<link rel="stylesheet" href="~/bundles/styles.min.css" />
Jednak udało mi się to zadziałać, zmieniając to na:
@Styles.Render("~/bundles/styles.min.css")
Metoda Styles.Render pobiera ciąg zapytania na końcu nazwy pliku, który, jak sądzę, jest kluczem pamięci podręcznej opisanym przez Hao powyżej.
Dla mnie to było takie proste. Mam nadzieję, że pomoże to każdemu takiemu jak ja, który szukał tego w Google przez wiele godzin i mógł znaleźć tylko posty sprzed kilku lat!
źródło