BundleCollection'ı MVC4'te önbelleğe alınmış komut dosyası paketlerini temizlemeye zorlama


85

... ya da endişelenmeyi bırakıp Microsoft'tan tamamen belgelenmemiş API'lere karşı kod yazmayı nasıl öğrendim . Resmi System.Web.Optimizationsürümün gerçek bir dokümantasyonu var mı ? Çünkü hiçbirini bulamıyorum, XML dokümanı yok ve tüm blog yazıları büyük ölçüde farklı olan RC API'ye atıfta bulunuyor. Her neyse ..

Javascript bağımlılıklarını otomatik olarak çözmek için bazı kodlar yazıyorum ve bu bağımlılıklardan anında paketler oluşturuyorum. Uygulamayı yeniden başlatmadan komut dosyalarını düzenlerseniz veya bir paketi etkileyecek değişiklikler yapmazsanız, her şey harika çalışır, değişiklikler yansıtılmaz. Bu yüzden, geliştirmede kullanmak üzere bağımlılıkların önbelleğe alınmasını devre dışı bırakma seçeneği ekledim.

Ancak, görünüşe göre paket koleksiyonu değişmiş olsa bileBundleTables URL'yi önbelleğe alıyor . Örneğin, bir paketi yeniden oluşturmak istediğimde kendi kodumda şöyle bir şey yapıyorum:

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

Aynı takma ada sahip bir paketi kaldırdığımda ve yeniden oluşturduğumda kesinlikle hiçbir şey olmuyor: bundleUrlgeri gönderilenler ResolveBundleUrl, paketi kaldırıp yeniden oluşturmadan önceki ile aynı. "Aynı" derken, içerik karmasının paketin yeni içeriğini yansıtacak şekilde değiştirilmediğini kastediyorum.

düzenle ... aslında, bundan çok daha kötü. Demeti kendisi her nasılsa dışında önbelleğe alınmış olan Bundleskoleksiyon. Tarayıcının komut dosyasını önbelleğe almasını önlemek için kendi rastgele karmamı oluşturursam, ASP.NET eski komut dosyasını döndürür . Görünüşe göre, bir desteyi çıkarmak BundleTable.Bundlesaslında hiçbir şey yapmıyor.

Bu sorunu aşmak için takma adı basitçe değiştirebilirim ve bu geliştirme için sorun değil, ancak bu fikirden hoşlanmıyorum çünkü bu, her sayfa yüklemeden sonra takma adları kullanımdan kaldırmam veya boyut olarak büyüyen bir BundleCollection her sayfa yüklemesi. Bunu bir üretim ortamında bırakırsanız felaket olur.

Öyle görünüyor ki, bir komut dosyası sunulduğunda, gerçek BundleTables.Bundlesnesneden bağımsız olarak önbelleğe alınır . Dolayısıyla, bir URL'yi yeniden kullanırsanız, bahsettiği paketi yeniden kullanmadan önce kaldırmış olsanız bile, önbelleğindekiyle yanıt verir ve Bundlesnesneyi değiştirmek önbelleği boşaltmaz - bu nedenle yalnızca yeni öğeler (veya bunun yerine, farklı bir ada sahip yeni öğeler) kullanılacaktır.

Davranış tuhaf görünüyor ... Koleksiyondan bir şey kaldırmak onu önbellekten kaldırmalıdır. Ama öyle değil. Bu önbelleği temizlemenin ve BundleCollectiono pakete ilk erişildiğinde önbelleğe aldığı içerik yerine mevcut içeriğini kullanmasının bir yolu olmalıdır .

Bunu nasıl yapacağım hakkında bir fikrin var mı?

Orada bu ResetAllbilinmeyen bir amacı vardır yöntem ama sadece o değildir ki zaten şeyleri kırar.


Burada da aynı problem. Sanırım benimkini çözmeyi başardım. Sizin için işe yarayıp yaramadığını deneyin ve bir göz atın. Tamamen katılıyorum. System.Web.Optimization için dokümantasyon çöptür ve internette bulabileceğiniz tüm örnekler güncel değildir.
LeftyX

2
+1, MS'in güven beklentisi hakkında keskin yorumlarla birlikte en üstte harika bir referans için. Ve ayrıca cevabını istediğim soruyu sorduğum için.
Raif

Yanıtlar:


33

Dokümantasyon konusunda acınızı duyuyoruz, maalesef bu özellik hala oldukça hızlı değişiyor ve dokümantasyon oluşturmanın biraz gecikmesi var ve neredeyse anında güncelliğini yitirebilir. Rick'in blog yazısı güncel ve bu arada güncel bilgileri yaymak için buradaki soruları da cevaplamaya çalıştım. Şu anda, her zaman güncel belgelere sahip olacak resmi kodepleks sitemizi kurma sürecindeyiz.

Şimdi, paketlerin önbellekten nasıl temizleneceğine ilişkin özel sorununuzla ilgili olarak.

  1. Paketlenmiş yanıtı ASP.NET önbelleğinin içinde, istenen paket url'sinden oluşturulan bir anahtar kullanarak depolarız, yani Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]bu paketi oluşturmak için kullanılan tüm dosyalar ve dizinlere karşı önbellek bağımlılıkları da kurarız. Bu nedenle, temeldeki dosyalardan veya dizinlerden herhangi biri değişirse, önbellek girdisi temizlenir.

  2. BundleTable / BundleCollection'ın istek başına canlı olarak güncellenmesini gerçekten desteklemiyoruz. Tam olarak desteklenen senaryo, paketlerin uygulama başlangıcı sırasında yapılandırılmasıdır (bu, web grubu senaryosunda her şeyin düzgün çalıştığı anlamına gelir, aksi takdirde bazı paket istekleri yanlış sunucuya gönderilirse 404'lere dönüşür). Kod örneğinize baktığımda, tahminimce paket koleksiyonunu belirli bir talep üzerine dinamik olarak değiştirmeye çalışıyorsunuz? Her türlü paket yönetimi / yeniden yapılandırmasına, her şeyin doğru şekilde kurulduğundan emin olmak için bir uygulama alanı sıfırlaması eşlik etmelidir.

Bu nedenle, uygulama alanınızı geri dönüştürmeden paket tanımlarınızı değiştirmekten kaçının. Paketlerinizin içindeki gerçek dosyaları değiştirmekte özgürsünüz, bunlar otomatik olarak algılanmalı ve paket URL'leriniz için yeni karma kodlar üretmelidir.


2
Doğrudan bilginizi buraya taşıdığınız için teşekkür ederiz! Evet - Paket koleksiyonunu dinamik olarak değiştirmeye çalışıyorum. Paketler, başka bir komut dosyasında açıklanan bir dizi bağımlılık temel alınarak oluşturulmuştur (yani kendisi, mutlaka paketin bir parçası değildir) - bu yüzden bu sorunu yaşıyorum. Bir paketteki bir betiği değiştirmek bir yıkamayı zorlayacağından, bu yapılabilir - manuel bir yıkama yöntemi ekleme olasılığı var mı? Bu çok önemli değil - bu, geliştirme sırasında kolaylık sağlamak için - ancak üretimde yanlışlıkla kullanılırsa sorunlara neden olabilecek kod oluşturmaktan nefret ediyorum.
Jamie Treworgy

Ayrıca web çiftliği konusunu da detaylandırabilir misiniz? Uygulama başladıktan sonra yeni bir paket eklemek , yalnızca oluşturulduğu sunucuda kullanılabilir olmasına mı yoksa yalnızca mevcut bir paketi değiştirmeye mi çalışmasına neden olur? Bu, bağımlılıkların çalışma zamanı çözümlemesini yapması gerektiğinden yapmaya çalıştığım şey için biraz dealkiller olurdu.
Jamie Treworgy

Elbette, açık bir önbellek temizleme eşdeğer yöntemi ekleyebiliriz, zaten dahili olarak var. Web çiftliği sorunuyla ilgili olarak, temelde iki web sunucunuz olduğunu hayal edin A ve B, isteğiniz paketi ekleyen ve yanıtı gönderen A'ya gidiyor, istemciniz şimdi paketin içeriğini almaya gidiyor, ancak istek şu adrese gidiyor: paketi kaydettirmeyen B sunucusu ve sizin 404'ünüz var.
Hao Kung

1
Önbellek güncellemesi tembeldir, paket ilk kez kullanıldığında (tipik olarak pakete bir başvuru oluşturarak) önbelleğe eklenir. İstekleri işlemeye başlamadan önce tüm web sunucularında paketlerinizi kurduğunuz eşdeğer bir uygulama başlatma kancanız varsa, bu iyi olacaktır.
Hao Kung

2
Bildiğim kadarıyla bu işe yaramıyor. Yani, kurucu dosya (lar) ı değiştirirsem, sunucu önbelleği burada belirtildiği gibi temizlenmez. Orada herhangi bir değişiklik elde etmek için şeyi geri dönüştürmelisiniz. Bu resmi belgelerin gerçekte nerede olduğunu bilen var mı?
Temmuz'13

21

Benzer bir problemim var.
Sınıfımda BundleConfigkullanmanın etkisinin ne olduğunu görmeye çalışıyordum BundleTable.EnableOptimizations = true.

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

        bundles.Add(...);
    }
}

Her şey yolunda gidiyordu.
Bir noktada bazı hata ayıklama yapıyordum ve özelliği false olarak ayarladım.
Ne olduğunu anlamakta zorlandım çünkü jquery için paket (ilki) çözülmeyecek ve yüklenmeyecekmiş gibi görünüyordu ( /bundles/jquery?v=).

Biraz küfür ettikten sonra sanırım (?!) İşleri halletmeyi başardım. Kaydın başında eklemeye çalışın bundles.Clear()ve bundles.ResetAll()işler yeniden çalışmaya başlamalıdır.

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Clear();
        bundles.ResetAll();

        BundleTable.EnableOptimizations = false;

        bundles.Add(...);
    }
}

Bu iki yöntemi yalnızca EnableOptimizationsözelliği değiştirdiğimde çalıştırmam gerektiğini fark ettim .

GÜNCELLEME:

Kazı derin Bunu öğrendim BundleTable.Bundles.ResolveBundleUrlve @Scripts.Urlpaket yolunu çözmek için sorunlar var gibi görünüyor.

Basitlik adına birkaç resim ekledim:

resim 1

Optimizasyonu kapattım ve birkaç komut dosyasını bir araya getirdim.

resim 2

Aynı paket gövdeye dahildir.

resim 3

@Scripts.Url bana paketin "optimize edilmiş" yolunu verirken @Scripts.Renderuygun olanı oluştururken .
Aynı şey olur BundleTable.Bundles.ResolveBundleUrl.

Visual Studio 2010 + MVC 4 + Framework .Net 4.0 kullanıyorum.


Hmm ... mesele şu ki, paket tablosunu gerçekten temizlemek istemiyorum, çünkü farklı sayfalardan (farklı bağımlılık kümelerinden oluşturulmuş) çok sayıda başka tablo içerecek. Ama bu gerçekten sadece bir geliştirme ortamında çalışmak için olduğu için, önbelleği boşaltacaksa içeriğini kopyalayıp sonra temizleyip sonra tekrar ekleyebilirim. Korkunç derecede verimsiz ama işe yararsa, geliştirme için yeterince iyi.
Jamie Treworgy

Katılıyorum ama sahip olduğum tek seçenek bu. Bütün öğleden sonrayı sorunun ne olduğunu anlamaya çalışarak geçirdim.
LeftyX

2
Sadece denedim, HALA önbelleği temizlemiyor !! Bunu temizleyin ResetAllve ayar denedim EnableOptimizationsben önbellek, hiçbir şey olay sıfırlamak için gerektiğinde başlangıçta ve satır içi hem false. Argh.
Jamie Treworgy

Geliştirici, bu nesnelerdeki yöntemler hakkında tek
satırlık bir yazıyla

6
Bu yöntemlerin ne işe yaradığını açıklamak gerekirse: Scripts.Url yalnızca BundleTable.Bundles.ResolveBundleUrl için bir takma addır, aynı zamanda paket olmayan url'leri de çözecektir, bu nedenle paketler hakkında bilgi sahibi olan genel bir url çözücüdür. Scripts.Render, paketlere veya paketi oluşturan bileşenlere bir başvuru oluşturup oluşturmayacağını belirlemek için EnableOptimizations bayrağını kullanır.
Hao Kung

8

Hao Kung'un web çiftliği senaryoları nedeniyle bunu yapmama tavsiyelerini akılda tutarak, bunu yapmak isteyebileceğiniz birçok senaryo olduğunu düşünüyorum. İşte bir çözüm:

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

Yukarıdaki kodu istediğiniz zaman arayabilirsiniz ve paketleriniz güncellenecektir. Bu, hem EnableOptimizations doğru hem de yanlış olduğunda çalışır - başka bir deyişle, bu, hata ayıklama veya canlı senaryolarda doğru işaretlemeyi atar.

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

Daha fazla okuma burada önbelleğe alma hakkında biraz konuşur veGenerateBundleResponse
Zac

4

Ayrıca paketleri yeniden oluşturmadan güncellerken sorunlarla karşılaştım. İşte anlaşılması gereken önemli şeyler:

  • Dosya yolları değişirse paket güncellenmez.
  • Paketin sanal yolu değişirse paket güncellenir.
  • Diskteki dosyalar değişirse paket güncellenir.

Öyleyse, dinamik gruplama yapıyorsanız, paketin sanal yolunun dosya yollarına dayalı olmasını sağlamak için bazı kodlar yazabilirsiniz. Dosya yollarına hashing uygulamanızı ve bu hash'i paketin sanal yolunun sonuna eklemenizi öneririm. Bu şekilde dosya yolları değiştiğinde sanal yol ve paket de güncellenir.

İşte benim için sorunu çözen bulduğum kod:

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

RessamınAggregate doğasında bulunan Schlemiel the Painter algoritmasını tekrar tekrar kullanırken birinin düşünmemesi riski nedeniyle, genellikle dize birleştirmeden kaçınmanızı öneririm +. Bunun yerine yapın string.Join("", filePaths). Bu, çok büyük girdiler için bile bu sorunu yaratmayacaktır.
ErikE

3

( StyleBundle veya ScriptBundle ) ' dan türetmeyi denediniz mi , yapıcınıza hiçbir dahil etme eklemeyi ve ardından geçersiz kılma

public override IEnumerable<System.IO.FileInfo> EnumerateFiles(BundleContext context)

Bunu dinamik stil sayfaları için yapıyorum ve EnumerateFiles her istek üzerine çağrılıyor. Muhtemelen en iyi çözüm değil ama işe yarıyor.


0

Ölü bir iş parçacığını yeniden canlandırdığım için özür dilerim, ancak kullanıcı arka uçtaki güzel sürümü değiştirdiğinde stil sayfalarının / komut dosyalarının otomatik olarak küçültülmesini istediğim bir Umbraco sitesinde Bundle önbelleğe alma ile benzer bir sorunla karşılaştım.

Zaten sahip olduğum kod (stil sayfası için onSaved yönteminde):

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

ve (onApplicationStarted):

BundleTable.EnableOptimizations = true;

Ne denersem deneyeyim, "~ / bundles / styles.min.css" dosyası değişmiş gibi görünmüyordu. Sayfamın başlığında, başlangıçta stil sayfasına şu şekilde yüklüyordum:

<link rel="stylesheet" href="~/bundles/styles.min.css" />

Ancak, bunu şu şekilde değiştirerek çalıştırdım:

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

Styles.Render yöntemi, Hao tarafından yukarıda açıklanan önbellek anahtarı olduğunu tahmin ettiğim dosya adının sonunda bir sorgu dizesi çekiyor.

Benim için bu kadar basitti. Umarım bu, benim gibi bunu saatlerce araştıran ve yalnızca birkaç yıllık gönderileri bulabilen herkese yardımcı olur!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.