... ou comment j'ai appris à cesser de m'inquiéter et à écrire simplement du code contre des API complètement non documentées de Microsoft . Existe-t-il une documentation réelle de la System.Web.Optimization
version officielle ? Parce que je ne peux certainement pas en trouver, il n'y a pas de documents XML et tous les articles de blog font référence à l'API RC qui est sensiblement différente. Anyhoo ..
J'écris du code pour résoudre automatiquement les dépendances javascript et je crée des bundles à la volée à partir de ces dépendances. Tout fonctionne très bien, sauf si vous modifiez des scripts ou apportez des modifications qui affectent un bundle sans redémarrer l'application, les modifications ne seront pas reflétées. J'ai donc ajouté une option pour désactiver la mise en cache des dépendances à utiliser dans le développement.
Cependant, apparemment BundleTables
met en cache l'URL même si la collection de bundles a changé . Par exemple, dans mon propre code, lorsque je souhaite recréer un bundle, je fais quelque chose comme ceci:
// 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"
Chaque fois que je supprime et recrée un bundle avec le même alias , absolument rien ne se passe: le bundleUrl
retour de ResolveBundleUrl
est le même qu'avant de supprimer et recréer le bundle. Par «le même», je veux dire que le hachage du contenu est inchangé pour refléter le nouveau contenu du bundle.
modifier ... en fait, c'est bien pire que ça. Le bundle lui - même est mis en cache en dehors de la Bundles
collection. Si je génère simplement mon propre hachage aléatoire pour empêcher le navigateur de mettre en cache le script, ASP.NET renvoie l'ancien script . Donc, apparemment, supprimer un bundle de BundleTable.Bundles
ne fait rien.
Je peux simplement changer l'alias pour contourner ce problème, et c'est OK pour le développement, mais je n'aime pas cette idée car cela signifie soit que je dois déprécier les alias après chaque chargement de page, soit avoir un BundleCollection qui grossit sur chaque chargement de page. Si vous laissiez cela activé dans un environnement de production, ce serait un désastre.
Il semble donc que lorsqu'un script est servi, il est mis en cache indépendamment de l' BundleTables.Bundles
objet réel . Donc, si vous réutilisez une URL, même si vous avez supprimé le bundle auquel elle faisait référence avant de la réutiliser, elle répond avec ce qui se trouve dans son cache, et la modification de l' Bundles
objet ne vide pas le cache - donc seuls les nouveaux éléments (ou plutôt, de nouveaux éléments avec un nom différent) seraient jamais utilisés.
Le comportement semble étrange ... supprimer quelque chose de la collection devrait le supprimer du cache. Mais ce n'est pas le cas. Il doit y avoir un moyen de vider ce cache et de lui faire utiliser le contenu actuel du BundleCollection
plutôt que ce qu'il a mis en cache lors du premier accès à ce bundle.
Une idée de comment je ferais ça?
Il y a cette ResetAll
méthode qui a un but inconnu mais qui casse les choses de toute façon, donc ce n'est pas ça.
la source
Réponses:
Nous entendons votre douleur sur la documentation, malheureusement, cette fonctionnalité change encore assez rapidement, et la génération de la documentation a un certain retard et peut être obsolète presque immédiatement. Le billet de blog de Rick est à jour, et j'ai essayé de répondre aux questions ici aussi pour diffuser des informations actuelles entre-temps. Nous sommes actuellement en train de mettre en place notre site officiel codeplex qui aura une documentation toujours à jour.
Maintenant, en ce qui concerne votre problème spécifique de savoir comment vider les bundles du cache.
Nous stockons la réponse groupée à l'intérieur du cache ASP.NET en utilisant une clé générée à partir de l'url du bundle demandée, c'est-à-dire que
Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]
nous configurons également des dépendances de cache contre tous les fichiers et répertoires qui ont été utilisés pour générer ce bundle. Donc, si l'un des fichiers ou répertoires sous-jacents change, l'entrée du cache sera vidée.Nous ne prenons pas vraiment en charge la mise à jour en direct de BundleTable / BundleCollection sur une base par demande. Le scénario entièrement pris en charge est que les bundles sont configurés lors du démarrage de l'application (c'est pour que tout fonctionne correctement dans le scénario de la batterie de serveurs Web, sinon certaines demandes de bundle finiraient par être 404 si elles étaient envoyées au mauvais serveur). En regardant votre exemple de code, je suppose que vous essayez de modifier dynamiquement la collection de bundles sur une demande particulière? Tout type d'administration / reconfiguration de bundle doit être accompagné d'une réinitialisation du domaine d'application pour garantir que tout a été configuré correctement.
Évitez donc de modifier vos définitions de bundle sans recycler le domaine de votre application. Vous êtes libre de modifier les fichiers réels à l'intérieur de vos bundles, qui devraient être automatiquement détectés et de générer de nouveaux hashcodes pour vos URL de bundle.
la source
J'ai un problème similaire.
Dans ma classe,
BundleConfig
j'essayais de voir quel était l'effet de l'utilisationBundleTable.EnableOptimizations = true
.public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { BundleTable.EnableOptimizations = true; bundles.Add(...); } }
Tout fonctionnait bien.
À un moment donné, je faisais du débogage et définissais la propriété sur false.
J'ai eu du mal à comprendre ce qui se passait car il semblait que le bundle pour jquery (le premier) ne serait pas résolu et chargé (
/bundles/jquery?v=
).Après quelques jurons, je pense (?!) Que j'ai réussi à arranger les choses. Essayez d'ajouter
bundles.Clear()
etbundles.ResetAll()
au début de l'enregistrement et les choses devraient recommencer à fonctionner.public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Clear(); bundles.ResetAll(); BundleTable.EnableOptimizations = false; bundles.Add(...); } }
J'ai réalisé que je devais exécuter ces deux méthodes uniquement lorsque je modifiais la
EnableOptimizations
propriété.MISE À JOUR:
En creusant plus profondément, j'ai découvert cela
BundleTable.Bundles.ResolveBundleUrl
et@Scripts.Url
semble avoir des problèmes pour résoudre le chemin du bundle.Par souci de simplicité, j'ai ajouté quelques images:
J'ai désactivé l'optimisation et regroupé quelques scripts.
Le même paquet est inclus dans le corps.
@Scripts.Url
me donne le chemin "optimisé" du bundle tandis que@Scripts.Render
génère le chemin approprié.La même chose arrive avec
BundleTable.Bundles.ResolveBundleUrl
.J'utilise Visual Studio 2010 + MVC 4 + Framework .Net 4.0.
la source
ResetAll
et j'ai essayé de réglerEnableOptimizations
sur false à la fois au démarrage et en ligne lorsque j'ai besoin de réinitialiser le cache, rien ne se passe. Argh.En gardant à l'esprit les recommandations de Hao Kung de ne pas faire cela à cause des scénarios de ferme Web, je pense qu'il existe de nombreux scénarios dans lesquels vous pourriez vouloir faire cela. Voici une solution:
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);
Vous pouvez appeler le code ci-dessus à tout moment et vos forfaits seront mis à jour. Cela fonctionne à la fois lorsque EnableOptimizations est vrai ou faux - en d'autres termes, cela jettera le balisage correct dans les scénarios de débogage ou en direct, avec:
@Scripts.Render("~/bundles/your-bundle-virtual-path")
la source
GenerateBundleResponse
J'ai également rencontré des problèmes avec la mise à jour des bundles sans reconstruction. Voici les choses importantes à comprendre:
Donc, sachant que, si vous effectuez un regroupement dynamique, vous pouvez écrire du code pour que le chemin virtuel du bundle soit basé sur les chemins de fichiers. Je recommande de hacher les chemins de fichiers et d'ajouter ce hachage à la fin du chemin virtuel du bundle. De cette façon, lorsque les chemins de fichiers changent, le chemin virtuel change également et le bundle se met à jour.
Voici le code avec lequel je me suis retrouvé et qui a résolu le problème pour moi:
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; }
la source
Aggregate
la concaténation de chaînes, en raison du risque que quelqu'un ne pense pas à l' algorithme intrinsèque de Schlemiel le peintre en utilisant à plusieurs reprises+
. Au lieu de cela, faites-lestring.Join("", filePaths)
. Cela n'aura pas ce problème, même pour les très gros intrants.Avez-vous essayé de dériver de ( StyleBundle ou ScriptBundle ), en ajoutant aucune inclusion dans votre constructeur, puis en remplaçant
public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)
Je fais cela pour les feuilles de style dynamiques et EnumerateFiles est appelé à chaque demande. Ce n'est probablement pas la meilleure solution, mais cela fonctionne.
la source
Toutes mes excuses pour raviver un fil mort, mais j'ai rencontré un problème similaire avec la mise en cache de Bundle dans un site Umbraco où je voulais que les feuilles de style / scripts soient automatiquement réduits lorsque l'utilisateur a changé la jolie version dans le backend.
Le code que j'avais déjà était (dans la méthode onSaved pour la feuille de style):
BundleTable.Bundles.Add(new StyleBundle("~/bundles/styles.min.css").Include( "~/css/main.css" ));
et (onApplicationStarted):
BundleTable.EnableOptimizations = true;
Peu importe ce que j'ai essayé, le fichier "~ / bundles / styles.min.css" n'a pas semblé changer. Dans la tête de ma page, je chargeais à l'origine dans la feuille de style comme ceci:
<link rel="stylesheet" href="~/bundles/styles.min.css" />
Cependant, je l'ai fait fonctionner en changeant cela en:
@Styles.Render("~/bundles/styles.min.css")
La méthode Styles.Render extrait une chaîne de requête à la fin du nom de fichier qui, je suppose, est la clé de cache décrite par Hao ci-dessus.
Pour moi, c'était aussi simple que ça. J'espère que cela aide quelqu'un d'autre comme moi qui a cherché sur Google pendant des heures et qui n'a pu trouver que des messages vieux de plusieurs années!
la source