... ou como aprendi a parar de me preocupar e apenas escrever código em APIs totalmente não documentadas da Microsoft . Existe alguma documentação real do System.Web.Optimization
lançamento oficial ? Porque eu com certeza não consigo encontrar nenhum, não há documentos XML e todas as postagens do blog se referem à RC API, que é substancialmente diferente. Anyhoo ..
Estou escrevendo um código para resolver automaticamente as dependências de javascript e estou criando pacotes dinâmicos a partir dessas dependências. Tudo funciona muito bem, exceto se você editar scripts ou fizer alterações que afetariam um pacote sem reiniciar o aplicativo, as alterações não serão refletidas. Portanto, adicionei uma opção para desativar o armazenamento em cache das dependências para uso no desenvolvimento.
No entanto, aparentemente BundleTables
armazena em cache a URL, mesmo que a coleção do pacote tenha mudado . Por exemplo, em meu próprio código, quando quero recriar um pacote, faço algo assim:
// 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"
Sempre que removo e recrio um pacote com o mesmo alias , absolutamente nada acontece: o bundleUrl
retornado de ResolveBundleUrl
é o mesmo de antes de remover e recriar o pacote. Por "o mesmo", quero dizer que o hash do conteúdo não é alterado para refletir o novo conteúdo do pacote.
editar ... na verdade, é muito pior do que isso. O pacote em si é armazenado em cache de alguma forma fora da Bundles
coleção. Se eu apenas gerar meu próprio hash aleatório para evitar que o navegador armazene o script em cache, o ASP.NET retorna o script antigo . Portanto, aparentemente, remover um pacote de BundleTable.Bundles
não faz nada.
Posso simplesmente mudar o alias para contornar este problema, e isso é bom para o desenvolvimento, mas não gosto dessa ideia, pois significa que tenho que descontinuar os aliases após cada carregamento de página ou tenho um BundleCollection que aumenta de tamanho em cada carregamento de página. Se você deixar isso ativado em um ambiente de produção, será um desastre.
Portanto, parece que quando um script é servido, ele é armazenado em cache independentemente do BundleTables.Bundles
objeto real . Portanto, se você reutilizar um URL, mesmo que tenha removido o pacote ao qual ele se refere antes de reutilizá-lo, ele responde com o que quer que esteja em seu cache e alterar o Bundles
objeto não limpa o cache - portanto, apenas novos itens (ou em vez disso, novos itens com um nome diferente) seriam usados.
O comportamento parece estranho ... remover algo da coleção deve removê-lo do cache. Mas isso não acontece. Deve haver uma maneira de liberar esse cache e fazer com que ele use o conteúdo atual doBundleCollection
vez do que ele armazenou em cache quando o pacote foi acessado pela primeira vez.
Alguma ideia de como eu faria isso?
Existe esse ResetAll
método que tem um propósito desconhecido, mas ele simplesmente quebra as coisas de qualquer maneira, então não é isso.
fonte
Respostas:
Nós ouvimos sua preocupação com a documentação, infelizmente esse recurso ainda está mudando muito rápido e a geração da documentação tem algum atraso e pode estar desatualizada quase imediatamente. A postagem do blog de Rick está atualizada, e eu tentei responder a perguntas aqui também para divulgar informações atuais nesse meio tempo. No momento, estamos em processo de criação de nosso site codeplex oficial, que sempre terá a documentação atualizada.
Agora, com relação à sua questão específica de como liberar pacotes do cache.
Armazenamos a resposta agrupada dentro do cache ASP.NET usando uma chave gerada a partir do url do pacote solicitado, ou seja
Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]
, também configuramos dependências de cache em relação a todos os arquivos e diretórios que foram usados para gerar este pacote. Portanto, se algum dos arquivos ou diretórios subjacentes mudar, a entrada do cache será liberada.Na verdade, não oferecemos suporte à atualização em tempo real do BundleTable / BundleCollection por solicitação. O cenário com suporte total é que os pacotes são configurados durante o início do aplicativo (isso é para que tudo funcione corretamente no cenário de farm da web, caso contrário, algumas solicitações de pacote acabariam sendo 404 se enviadas para o servidor errado). Olhando seu exemplo de código, meu palpite é que você está tentando modificar a coleção do pacote dinamicamente em uma solicitação específica? Qualquer tipo de administração / reconfiguração de bundle deve ser acompanhada por uma redefinição de appdomain para garantir que tudo foi configurado corretamente.
Portanto, evite modificar as definições do pacote sem reciclar o domínio do aplicativo. Você é livre para modificar os arquivos reais dentro de seus pacotes, que devem ser detectados automaticamente e gerar novos códigos de hash para os urls de seus pacotes.
fonte
Eu tenho um problema semelhante.
Na minha aula,
BundleConfig
eu estava tentando ver qual era o efeito do usoBundleTable.EnableOptimizations = true
.public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { BundleTable.EnableOptimizations = true; bundles.Add(...); } }
Tudo estava funcionando bem.
Em algum ponto, eu estava fazendo uma depuração e defini a propriedade como false.
Lutei para entender o que estava acontecendo porque parecia que o pacote do jquery (o primeiro) não seria resolvido e carregado (
/bundles/jquery?v=
).Depois de alguns xingamentos, acho (?!) consegui resolver as coisas. Tente adicionar
bundles.Clear()
ebundles.ResetAll()
no início do registro e as coisas devem começar a funcionar novamente.public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Clear(); bundles.ResetAll(); BundleTable.EnableOptimizations = false; bundles.Add(...); } }
Percebi que preciso executar esses dois métodos apenas quando alterar a
EnableOptimizations
propriedade.ATUALIZAR:
Indo mais fundo, descobri isso
BundleTable.Bundles.ResolveBundleUrl
e@Scripts.Url
pareço ter problemas para resolver o caminho do pacote.Para simplificar, adicionei algumas imagens:
Desativei a otimização e agrupei alguns scripts.
O mesmo pacote está incluído no corpo.
@Scripts.Url
me dá o caminho "otimizado" do pacote enquanto@Scripts.Render
gera o apropriado.A mesma coisa acontece com
BundleTable.Bundles.ResolveBundleUrl
.Estou usando o Visual Studio 2010 + MVC 4 + Framework .Net 4.0.
fonte
ResetAll
, e tentei definirEnableOptimizations
como falso na inicialização e em linha quando preciso redefinir o cache, mas nada aconteceu. Argh.Tendo em mente as recomendações de Hao Kung para não fazer isso por causa de cenários de web farm, acho que há muitos cenários em que você pode querer fazer isso. Aqui está uma solução:
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);
Você pode ligar para o código acima a qualquer momento e seus pacotes serão atualizados. Isso funciona tanto quando EnableOptimizations é verdadeiro ou falso - em outras palavras, isso descartará a marcação correta em cenários de depuração ou ao vivo, com:
@Scripts.Render("~/bundles/your-bundle-virtual-path")
fonte
GenerateBundleResponse
Também tive problemas com a atualização de pacotes sem reconstruir. Aqui estão as coisas importantes para entender:
Portanto, sabendo que, se você estiver fazendo empacotamento dinâmico, você pode escrever algum código para fazer com que o caminho virtual do pacote seja baseado nos caminhos de arquivo. Eu recomendo fazer o hash dos caminhos dos arquivos e anexar esse hash ao final do caminho virtual do pacote. Dessa forma, quando os caminhos dos arquivos mudam, o caminho virtual também muda e o pacote é atualizado.
Este é o código que acabei com e que resolveu o problema para mim:
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; }
fonte
Aggregate
para concatenação de strings, devido ao risco de alguém não pensar no algoritmo de Schlemiel, o Pintor inerente ao usar repetidamente+
. Em vez disso, apenas façastring.Join("", filePaths)
. Isso não terá esse problema, mesmo para entradas muito grandes.Você tentou derivar de ( StyleBundle ou ScriptBundle ), sem adicionar inclusões em seu construtor e, em seguida, sobrescrever
public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)
Eu faço isso para folhas de estilo dinâmicas e EnumerateFiles é chamado em cada solicitação. Provavelmente não é a melhor solução, mas funciona.
fonte
Peço desculpas por reviver um thread morto, no entanto, encontrei um problema semelhante com o cache de Bundle em um site de Umbraco, onde queria que as folhas de estilo / scripts diminuíssem automaticamente quando o usuário alterasse a bela versão no backend.
O código que eu já tinha era (no método onSaved para a folha de estilo):
BundleTable.Bundles.Add(new StyleBundle("~/bundles/styles.min.css").Include( "~/css/main.css" ));
e (onApplicationStarted):
BundleTable.EnableOptimizations = true;
Não importa o que eu tentei, o arquivo "~ / bundles / styles.min.css" não mudou. No cabeçalho da minha página, estava originalmente carregando na folha de estilo assim:
<link rel="stylesheet" href="~/bundles/styles.min.css" />
No entanto, comecei a trabalhar mudando isso para:
@Styles.Render("~/bundles/styles.min.css")
O método Styles.Render puxa uma string de consulta no final do nome do arquivo que eu imagino ser a chave de cache descrita por Hao acima.
Para mim, era simples assim. Espero que isso ajude alguém como eu, que estava pesquisando no Google por horas e só conseguiu encontrar postagens de vários anos!
fonte