Cómo forzar a BundleCollection a enjuagar paquetes de scripts en caché en MVC4

… o cómo aprendí a dejar de preocuparme y simplemente escribir código contra API completamente indocumentadas de Microsoft . ¿Hay alguna documentación real del lanzamiento oficial de System.Web.Optimization ? ‘Porque no puedo encontrar ninguno, no hay documentos XML, y todas las publicaciones del blog se refieren a la API RC, que es sustancialmente diferente. Anyhoo …

Estoy escribiendo un código para resolver automáticamente las dependencias de JavaScript y estoy creando paquetes sobre la marcha de esas dependencias. Todo funciona muy bien, excepto si edita scripts o realiza cambios que afectarían un paquete sin reiniciar la aplicación, los cambios no se reflejarán. Así que agregué una opción para desactivar el almacenamiento en caché de las dependencias para su uso en el desarrollo.

Sin embargo, aparentemente BundleTables caché la URL incluso si la colección de paquetes ha cambiado . Por ejemplo, en mi propio código cuando quiero volver a crear un paquete, hago algo como esto:

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

Cada vez que elimino y vuelvo a crear un paquete con el mismo alias , no ocurre absolutamente nada: el bundleUrl devuelto por ResolveBundleUrl es el mismo que antes de eliminar y volver a crear el paquete. Con “lo mismo” quiero decir que el hash de contenido no se modifica para reflejar los nuevos contenidos del paquete.

editar … en realidad, es mucho peor que eso. El paquete en sí mismo se almacena en caché de alguna manera fuera de la colección de Bundles . Si solo genero mi propio hash aleatorio para evitar que el navegador guarde en caché el script, ASP.NET devuelve el script anterior . Entonces, aparentemente, eliminar un paquete de BundleTable.Bundles realidad no hace nada.

Simplemente puedo cambiar el alias para evitar este problema, y ​​eso está bien para el desarrollo, pero no me gusta esa idea, ya que significa que tengo que desaprobar alias después de cada carga de página, o tengo un BundleCollection que crece en tamaño en cada carga de página. Si deja esto encendido en un entorno de producción, sería un desastre.

Por lo tanto, parece que cuando se BundleTables.Bundles un script, se almacena en caché independientemente del objeto BundleTables.Bundles real. Por lo tanto, si reutiliza una URL, incluso si ha eliminado el paquete al que se refería antes de volver a utilizarla, responde con todo lo que hay en su caché, y alterar el objeto Bundles no vacía la caché, por lo que solo los elementos nuevos ( o más bien, nuevos elementos con un nombre diferente) alguna vez se utilizaría.

El comportamiento parece extraño … eliminar algo de la colección debería eliminarlo de la memoria caché. Pero no es así Debe haber una forma de purgar este caché y hacer que use el contenido actual de BundleCollection lugar de almacenarlo en caché cuando se accedió por primera vez al paquete.

¿Alguna idea de cómo haría esto?

Existe este método ResetAll que tiene un propósito desconocido pero simplemente rompe las cosas de todos modos, así que no es así.

Escuchamos su dolor en la documentación, desafortunadamente esta característica todavía está cambiando bastante rápido, y la generación de documentación tiene cierto rezago, y puede estar desactualizada casi de inmediato. La publicación del blog de Rick está actualizada, y he intentado responder preguntas aquí también para difundir información actual mientras tanto. Actualmente estamos en el proceso de configurar nuestro sitio codeplex oficial que tendrá siempre la documentación actual.

Ahora, en lo que respecta a su problema específico de cómo vaciar los paquetes del caché.

  1. Almacenamos la respuesta agrupada dentro de la memoria caché ASP.NET usando una clave generada fuera de la url del paquete solicitada, es decir, Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"] también configuramos dependencias de caché contra todos los archivos y directorios que se usaron para generar este paquete. Por lo tanto, si alguno de los archivos o directorios subyacentes cambia, la entrada de caché se purgará.

  2. Realmente no admitimos actualizaciones en vivo de BundleTable / BundleCollection por solicitud. El escenario totalmente admitido es que los paquetes se configuran durante el inicio de la aplicación (esto es para que todo funcione correctamente en el escenario de la granja web, de lo contrario, algunas solicitudes de paquetes terminarían siendo 404 si se envían al servidor incorrecto). En cuanto a su ejemplo de código, supongo que está tratando de modificar la colección de paquetes de forma dinámica en una solicitud particular. Cualquier tipo de administración / reconfiguración del paquete debe ir acompañada de un reinicio del dominio de la aplicación para garantizar que todo se haya configurado correctamente.

Por lo tanto, evite modificar las definiciones de paquete sin tener que reciclar el dominio de su aplicación. Usted es libre de modificar los archivos reales dentro de sus paquetes, que deberían detectarse automáticamente y generar nuevos códigos hash para las URL de sus paquetes.

Tengo un problema similar.
En mi clase BundleConfig estaba tratando de ver cuál era el efecto de usar BundleTable.EnableOptimizations = true .

 public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { BundleTable.EnableOptimizations = true; bundles.Add(...); } } 

Todo estaba funcionando bien.
En algún momento, estaba depurando y estableciendo la propiedad como falsa.
Me costó entender qué estaba sucediendo porque parecía que el paquete para jquery (el primero) no se resolvería y cargaría ( /bundles/jquery?v= ).

Después de un juramento, creo (?!) que he logrado solucionar las cosas. Intente agregar bundles.Clear() y bundles.ResetAll() al comienzo del registro y las cosas deberían comenzar a funcionar nuevamente.

 public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Clear(); bundles.ResetAll(); BundleTable.EnableOptimizations = false; bundles.Add(...); } } 

Me di cuenta de que necesito ejecutar estos dos métodos solo cuando cambio la propiedad EnableOptimizations .

ACTUALIZAR:

Profundizando más Descubrí que BundleTable.Bundles.ResolveBundleUrl y @Scripts.Url parecen tener problemas para resolver la ruta del paquete.

En aras de la simplicidad, he agregado algunas imágenes:

imagen 1

He desactivado la optimización y he incluido algunos scripts.

imagen 2

El mismo paquete está incluido en el cuerpo.

imagen 3

@Scripts.Url me da la ruta “optimizada” del paquete mientras @Scripts.Render genera el correcto.
Lo mismo sucede con BundleTable.Bundles.ResolveBundleUrl .

Estoy usando Visual Studio 2010 + MVC 4 + Framework .Net 4.0.

Teniendo en cuenta las recomendaciones de Hao Kung de no hacer esto debido a los escenarios de las granjas web, creo que hay muchos escenarios en los que es posible que desee hacer esto. Aquí hay una solución:

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

Puede llamar al código anterior en cualquier momento y sus paquetes se actualizarán. Esto funciona tanto cuando EnableOptimizations es verdadero como falso; en otras palabras, arrojará el marcado correcto en escenarios de depuración o en vivo, con:

 @Scripts.Render("~/bundles/your-bundle-virtual-path") 

También encontré problemas con la actualización de paquetes sin reconstrucción. Aquí están las cosas importantes para entender:

  • El paquete NO se actualiza si cambian las rutas de archivos.
  • El paquete SI se actualiza si la ruta virtual del paquete cambia.
  • El paquete SI se actualiza si cambian los archivos en el disco.

Entonces, sabiendo que, si está haciendo un agrupamiento dynamic, puede escribir un código para hacer que la ruta virtual del paquete se base en las rutas de archivos. Recomiendo hash las rutas de archivos y anexar ese hash al final de la ruta virtual del paquete. De esta manera, cuando cambian las rutas de acceso de los archivos, la ruta virtual y el paquete se actualizarán.

Aquí está el código con el que terminé que resolvió el problema para mí:

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

¿Has intentado derivar de ( StyleBundle o ScriptBundle ), sin agregar inclusiones en tu constructor y luego invalidar

 public override IEnumerable EnumerateFiles(BundleContext context) 

Hago esto para hojas de estilo dinámicas y EnumerateFiles se llama en cada solicitud. Probablemente no sea la mejor solución, pero funciona.

Disculpas por revivir un hilo muerto, sin embargo, encontré un problema similar con el almacenamiento en caché de Bundle en un sitio de Umbraco en el que quería que las hojas de estilo / guiones se minimizaran automáticamente cuando el usuario cambiaba la versión bonita en el back-end.

El código que ya tenía era (en el método onSaved para la hoja de estilo):

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

y (onApplicationStarted):

 BundleTable.EnableOptimizations = true; 

No importa lo que intenté, el archivo “~ / bundles / styles.min.css” no pareció cambiar. En el encabezado de mi página, originalmente estaba cargando en la hoja de estilos de esta manera:

  

Sin embargo, lo hice funcionar cambiando esto a:

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

El método Styles.Render extrae una cadena de consulta al final del nombre de archivo, que supongo que es la clave de caché descrita por Hao anteriormente.

Para mí, fue tan simple como eso. Espero que esto ayude a cualquier otra persona como yo que estuvo buscando en Google durante horas y solo pudo encontrar publicaciones de varios años.

    Intereting Posts