... o cómo aprendí a dejar de preocuparme y simplemente escribir código contra API completamente indocumentadas de Microsoft . ¿Existe alguna documentación real del System.Web.Optimization
lanzamiento oficial ? Porque seguro que 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 a partir de esas dependencias. Todo funciona muy bien, excepto si edita scripts o realiza cambios que afectarían a un paquete sin reiniciar la aplicación, los cambios no se reflejarán. Así que agregué una opción para deshabilitar el almacenamiento en caché de las dependencias para su uso en desarrollo.
Sin embargo, aparentemente BundleTables
almacena en 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"
Siempre que elimino y vuelvo a crear un paquete con el mismo alias , no pasa absolutamente nada: el bundleUrl
devuelto ResolveBundleUrl
es el mismo que antes de eliminar y volver a crear el paquete. Por "lo mismo" me refiero a que el hash de contenido no se modifica para reflejar el nuevo contenido del paquete.
editar ... en realidad, es mucho peor que eso. El paquete en sí se almacena en caché de alguna manera fuera de la Bundles
colección. Si solo genero mi propio hash aleatorio para evitar que el navegador almacene en caché el script, ASP.NET devuelve el antiguo script . Entonces, aparentemente, quitar un paquete BundleTable.Bundles
no hace nada.
Simplemente puedo cambiar el alias para solucionar este problema, y eso está bien para el desarrollo, pero no me gusta esa idea, ya que significa que tengo que desaprobar los alias después de cada carga de página o tener un BundleCollection que crece en tamaño en cada carga de página. Si deja esto en un entorno de producción, sería un desastre.
Entonces, parece que cuando se sirve un script, se almacena en caché independientemente del BundleTables.Bundles
objeto real . Entonces, si reutiliza una URL, incluso si ha eliminado el paquete al que se refería antes de reutilizarlo, responde con lo que sea que esté en su caché, y la alteración del Bundles
objeto no vacía el caché, por lo que solo los elementos nuevos (o más bien, se usarían elementos nuevos con un nombre diferente).
El comportamiento parece extraño ... eliminar algo de la colección debería eliminarlo del caché. Pero no es así. Debe haber una forma de vaciar esta caché y hacer que use el contenido actual del en BundleCollection
lugar de lo que almacenó en caché cuando se accedió por primera vez a ese paquete.
¿Alguna idea de cómo haría esto?
Existe este ResetAll
método que tiene un propósito desconocido pero simplemente rompe cosas de todos modos, así que no es así.
fuente
Respuestas:
Escuchamos su dolor con la documentación, desafortunadamente, esta característica todavía está cambiando bastante rápido y la generación de documentación tiene cierto retraso y puede quedar desactualizada casi de inmediato. Publicación del blog de Rick está actualizada y, mientras tanto, he intentado responder preguntas aquí para difundir información actual. Actualmente estamos en el proceso de configurar nuestro sitio oficial del Codeplex que siempre tendrá documentación actualizada.
Ahora en lo que respecta a su problema específico de cómo vaciar los paquetes del caché.
Almacenamos la respuesta incluida dentro de la caché de ASP.NET utilizando una clave generada a partir de la URL del paquete solicitada, es decir
Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]
, también configuramos las dependencias de la caché contra todos los archivos y directorios que se utilizaron para generar este paquete. Entonces, si cambia alguno de los archivos o directorios subyacentes, la entrada de la caché se eliminará.Realmente no admitimos la actualización en vivo de BundleTable / BundleCollection por solicitud. El escenario totalmente compatible 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). Mirando su ejemplo de código, supongo que está intentando modificar la colección de paquetes de forma dinámica en una solicitud en particular. Cualquier tipo de administración / reconfiguración del paquete debe ir acompañado 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 su paquete sin reciclar el dominio de su aplicación. Puede 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.
fuente
Tengo un problema similar.
En mi clase
BundleConfig
estaba tratando de ver cuál era el efecto de consumirBundleTable.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 haciendo una depuración y establecí la propiedad en falso.
Me costó entender lo que estaba sucediendo porque parecía que el paquete de jquery (el primero) no se resolvería y cargaría (
/bundles/jquery?v=
).Después de algunas palabrotas, creo (?!) Que me las arreglé para arreglar las cosas. Intente agregar
bundles.Clear()
ybundles.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
EnableOptimizations
propiedad.ACTUALIZAR:
Profundizando, lo descubrí
BundleTable.Bundles.ResolveBundleUrl
y@Scripts.Url
parece que tengo problemas para resolver la ruta del paquete.En aras de la simplicidad, agregué algunas imágenes:
He desactivado la optimización y he incluido algunos scripts.
El mismo paquete está incluido en el cuerpo.
@Scripts.Url
me da la ruta "optimizada" del paquete mientras@Scripts.Render
genera la correcta.Lo mismo pasa con
BundleTable.Bundles.ResolveBundleUrl
.Estoy usando Visual Studio 2010 + MVC 4 + Framework .Net 4.0.
fuente
ResetAll
, y he intentado configurarloEnableOptimizations
en falso tanto al inicio como en línea cuando necesito restablecer el caché, no sucede nada. Argh.Teniendo en cuenta las recomendaciones de Hao Kung de no hacer esto debido a escenarios de granjas web, creo que hay muchos escenarios en los que es posible que desee hacer esto. He aquí 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 o falso; en otras palabras, esto arrojará el marcado correcto en escenarios de depuración o en vivo, con:
@Scripts.Render("~/bundles/your-bundle-virtual-path")
fuente
GenerateBundleResponse
También encontré problemas con la actualización de paquetes sin reconstruir. Estas son las cosas importantes que debe comprender:
Entonces, sabiendo eso, si está haciendo un paquete dinámico, puede escribir algún código para hacer que la ruta virtual del paquete se base en las rutas de los archivos. Recomiendo aplicar hash a las rutas de archivo y agregar ese hash al final de la ruta virtual del paquete. De esta manera, cuando cambian las rutas de archivo, también lo hace la ruta virtual y el paquete se actualizará.
Aquí está el código con el que terminé que me resolvió el problema:
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; }
fuente
Aggregate
concatenación de cadenas, debido al riesgo de que alguien no piense en el algoritmo inherente de Schlemiel the Painter al usarlo repetidamente+
. En su lugar, hazlostring.Join("", filePaths)
. Esto no tendrá ese problema, incluso para entradas muy grandes.¿Ha intentado derivar de ( StyleBundle o ScriptBundle ), sin agregar inclusiones en su constructor y luego anulando
public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)
Hago esto para hojas de estilo dinámicas y se llama a EnumerateFiles en cada solicitud. Probablemente no sea la mejor solución, pero funciona.
fuente
Disculpas por revivir un hilo muerto, sin embargo, encontré un problema similar con el almacenamiento en caché de Bundle en un sitio de Umbraco donde quería que las hojas de estilo / scripts se minimizaran automáticamente cuando el usuario cambiaba la versión bonita en el backend.
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 estilo así:
<link rel="stylesheet" href="~/bundles/styles.min.css" />
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 del 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!
fuente