Cách buộc BundleCollection xóa các gói tập lệnh được lưu trong bộ nhớ cache trong MVC4


85

... hoặc cách tôi học cách ngừng lo lắng và chỉ viết mã chống lại các API hoàn toàn không có tài liệu từ Microsoft . Có tài liệu thực tế nào về System.Web.Optimizationbản phát hành chính thức không? 'Vì tôi chắc chắn không thể tìm thấy bất kỳ tài liệu nào, không có tài liệu XML và tất cả các bài đăng trên blog đều đề cập đến API RC, về cơ bản là khác nhau. Anyhoo ..

Tôi đang viết một số mã để tự động giải quyết các phụ thuộc javascript và đang tạo các gói ngay lập tức từ các phụ thuộc đó. Mọi thứ đều hoạt động tốt, ngoại trừ trường hợp bạn chỉnh sửa tập lệnh hoặc thực hiện các thay đổi có thể ảnh hưởng đến một gói mà không khởi động lại ứng dụng, các thay đổi sẽ không được phản ánh. Vì vậy, tôi đã thêm một tùy chọn để tắt bộ nhớ đệm của các phụ thuộc để sử dụng trong phát triển.

Tuy nhiên, dường như BundleTablesURL lưu vào bộ nhớ cache ngay cả khi bộ sưu tập gói đã thay đổi . Ví dụ: trong mã của riêng tôi khi tôi muốn tạo lại một nhóm, tôi làm như sau:

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

Bất cứ khi nào tôi xóa và tạo lại một gói có cùng bí danh , hoàn toàn không có gì xảy ra: kết quả bundleUrltrả về ResolveBundleUrlgiống như trước khi tôi xóa và tạo lại gói. "Giống nhau", ý tôi là hàm băm nội dung không thay đổi để phản ánh nội dung mới của gói.

chỉnh sửa ... thực ra, nó còn tệ hơn thế nhiều. Bản thân gói được lưu trong bộ nhớ cache bằng cách nào đó bên ngoài Bundlesbộ sưu tập. Nếu tôi chỉ tạo mã băm ngẫu nhiên của riêng mình để ngăn trình duyệt lưu tập lệnh vào bộ nhớ đệm, ASP.NET sẽ trả về tập lệnh cũ . Vì vậy, rõ ràng, việc xóa một gói khỏi BundleTable.Bundlesthực sự không làm được gì cả.

Tôi chỉ có thể thay đổi bí danh để giải quyết vấn đề này và điều đó được chấp nhận để phát triển, nhưng tôi không thích ý tưởng đó vì nó có nghĩa là tôi phải ngừng sử dụng bí danh sau mỗi lần tải trang hoặc có BundleCollection tăng kích thước trên mỗi lần tải trang. Nếu bạn để điều này trong môi trường sản xuất, đó sẽ là một thảm họa.

Vì vậy, có vẻ như khi một tập lệnh được phân phát, nó sẽ được lưu vào bộ nhớ đệm độc lập với BundleTables.Bundlesđối tượng thực tế . Vì vậy, nếu bạn sử dụng lại một URL, ngay cả khi bạn đã xóa gói mà nó tham chiếu trước khi sử dụng lại, nó sẽ phản hồi với bất kỳ thứ gì trong bộ nhớ cache của nó và việc thay đổi Bundlesđối tượng không xóa bộ nhớ cache - vì vậy chỉ các mục mới (hoặc thay vào đó, các mục mới với một tên khác) sẽ được sử dụng.

Hành vi có vẻ kỳ quặc ... việc xóa nội dung nào đó khỏi bộ sưu tập sẽ xóa nó khỏi bộ nhớ cache. Nhưng nó không. Phải có một cách để xóa bộ nhớ cache này và để nó sử dụng nội dung hiện tại của nội dung BundleCollectionthay vì những gì nó đã lưu trong bộ nhớ cache khi gói đó được truy cập lần đầu.

Bất kỳ ý tưởng làm thế nào tôi sẽ làm điều này?

Có một ResetAllphương pháp này có mục đích không xác định nhưng dù sao nó cũng phá vỡ mọi thứ nên không phải vậy.


Cùng một vấn đề ở đây. Tôi nghĩ rằng tôi đã quản lý để giải quyết của tôi. Hãy thử và xem nếu nó phù hợp với bạn. Hoàn toàn đồng ý. Tài liệu cho System.Web.Optimization là rác rưởi và tất cả các mẫu bạn có thể tìm thấy trên internet đều đã lỗi thời.
LeftyX

2
+1 để tham khảo tuyệt vời ở trên cùng kết hợp với bình luận sâu sắc về kỳ vọng tin tưởng của MS. Và cũng để hỏi câu hỏi mà tôi muốn có câu trả lời.
Raif

Câu trả lời:


33

Chúng tôi nghe thấy nỗi đau của bạn về tài liệu, rất tiếc là tính năng này vẫn đang thay đổi khá nhanh và việc tạo tài liệu có một số độ trễ và có thể lỗi thời gần như ngay lập tức. Bài đăng trên blog của Rick đã được cập nhật và tôi đã cố gắng trả lời các câu hỏi ở đây cũng như để phổ biến thông tin hiện tại trong thời gian chờ đợi. Chúng tôi hiện đang trong quá trình thiết lập trang web codeplex chính thức của mình, trang web này sẽ luôn có tài liệu mới nhất.

Bây giờ liên quan đến vấn đề cụ thể của bạn về cách xóa các gói tạo thành bộ nhớ cache.

  1. Chúng tôi lưu trữ phản hồi theo gói bên trong bộ đệm ASP.NET bằng cách sử dụng khóa được tạo ra từ url gói được yêu cầu, tức là Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]chúng tôi cũng thiết lập các phần phụ thuộc bộ đệm dựa vào tất cả các tệp và thư mục được sử dụng để tạo gói này. Vì vậy, nếu bất kỳ tệp hoặc thư mục cơ bản nào thay đổi, mục nhập bộ nhớ cache sẽ bị xóa.

  2. Chúng tôi không thực sự hỗ trợ cập nhật trực tiếp BundleTable / BundleCollection trên cơ sở yêu cầu. Tình huống được hỗ trợ đầy đủ là các gói được định cấu hình trong khi khởi động ứng dụng (điều này để mọi thứ hoạt động bình thường trong kịch bản trang web, nếu không một số yêu cầu gói sẽ kết thúc là 404 nếu được gửi đến máy chủ sai). Nhìn vào ví dụ mã của bạn, tôi đoán là bạn đang cố gắng sửa đổi động bộ sưu tập theo một yêu cầu cụ thể? Bất kỳ loại quản trị / cấu hình lại gói nào cũng phải đi kèm với thiết lập lại miền ứng dụng để đảm bảo mọi thứ đã được thiết lập chính xác.

Vì vậy, hãy tránh sửa đổi định nghĩa gói mà không tái chế miền ứng dụng của bạn. Bạn có thể tự do sửa đổi các tệp thực bên trong các gói của mình, tệp đó sẽ tự động được phát hiện và tạo mã băm mới cho các url gói của bạn.


2
cảm ơn bạn đã mang kiến ​​thức trực tiếp của bạn để chịu ở đây! Có - Tôi đang cố gắng sửa đổi động bộ sưu tập. Các gói được xây dựng dựa trên một tập hợp các phụ thuộc được mô tả trong một tập lệnh khác (tức là chính nó, không nhất thiết là một phần của gói) - đó là lý do tại sao tôi gặp sự cố này. Vì việc thay đổi một tập lệnh nằm trong một gói sẽ buộc xả, nó có thể được thực hiện - có khả năng thêm phương pháp xả thủ công không? Điều này không quan trọng - điều này là để thuận tiện trong quá trình phát triển - nhưng tôi ghét việc tạo mã có thể gây ra sự cố nếu vô tình được sử dụng trên sản phẩm.
Jamie Treworgy

Ngoài ra, bạn có thể nói rõ hơn về vấn đề trang trại web không? Việc thêm một gói mới sau khi ứng dụng bắt đầu có dẫn đến việc nó chỉ khả dụng trên máy chủ mà nó đã được tạo - hay chỉ cố gắng thay đổi một gói hiện có? Điều này sẽ là một chút khó khăn cho những gì tôi đang cố gắng làm vì nó cần thực hiện giải quyết thời gian chạy của các phụ thuộc.
Jamie Treworgy

Chắc chắn, chúng tôi có thể thêm một phương pháp tương đương xóa bộ nhớ cache rõ ràng, nó đã có sẵn trong nội bộ. Về vấn đề trang web, về cơ bản, hãy tưởng tượng bạn có hai máy chủ web A và B, yêu cầu của bạn chuyển đến A, người thêm gói và gửi phản hồi xuống, khách hàng của bạn bây giờ sẽ tìm nạp nội dung của gói, nhưng rất tiếc, yêu cầu chuyển đến máy chủ B không đăng ký gói và có 404.
Hao Kung

1
Cập nhật bộ nhớ cache là lười biếng, lần đầu tiên gói được sử dụng (thường là thông qua hiển thị tham chiếu đến gói), nó sẽ được thêm vào bộ nhớ cache. Nếu bạn có một hook start ứng dụng tương đương, nơi bạn thiết lập các gói của mình trên tất cả các máy chủ web trước khi bắt đầu xử lý các yêu cầu, điều đó sẽ ổn.
Hao Kung

2
Theo như tôi có thể nói điều này không hoạt động. Nghĩa là, nếu tôi thay đổi (các) tệp cấu thành, bộ đệm ẩn máy chủ sẽ không bị xóa như đã nêu ở đây. Bạn phải tái chế thứ để có được bất kỳ thay đổi nào ở đó. Có ai biết tài liệu chính thức đó thực sự ở đâu không?
philw

21

Tôi có một vấn đề tương tự.
Trong lớp học của BundleConfigtôi, tôi đã cố gắng xem tác dụng của việc sử dụng là gì BundleTable.EnableOptimizations = true.

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

        bundles.Add(...);
    }
}

Mọi thứ đều hoạt động tốt.
Tại một số thời điểm, tôi đang thực hiện gỡ lỗi và đặt thuộc tính thành false.
Tôi cố gắng hiểu điều gì đang xảy ra vì có vẻ như gói cho jquery (gói đầu tiên) sẽ không được giải quyết và tải ( /bundles/jquery?v=).

Sau vài câu chửi thề, tôi nghĩ (?!) Tôi đã sắp xếp được mọi thứ. Cố gắng thêm bundles.Clear()bundles.ResetAll()khi bắt đầu đăng ký và mọi thứ sẽ bắt đầu hoạt động trở lại.

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

        BundleTable.EnableOptimizations = false;

        bundles.Add(...);
    }
}

Tôi nhận ra rằng tôi chỉ cần chạy hai phương thức này khi tôi thay đổi thuộc EnableOptimizationstính.

CẬP NHẬT:

Tìm hiểu sâu hơn, tôi phát hiện ra điều đó BundleTable.Bundles.ResolveBundleUrl@Scripts.Urldường như có vấn đề để giải quyết đường dẫn gói.

Để đơn giản hơn, tôi đã thêm một vài hình ảnh:

hình ảnh 1

Tôi đã tắt tối ưu hóa và gói một vài tập lệnh.

hình ảnh 2

Cùng một bó được bao gồm trong cơ thể.

hình ảnh 3

@Scripts.Urlcung cấp cho tôi đường dẫn "được tối ưu hóa" của gói trong khi @Scripts.Rendertạo đường dẫn thích hợp.
Điều tương tự cũng xảy ra với BundleTable.Bundles.ResolveBundleUrl.

Tôi đang sử dụng Visual Studio 2010 + MVC 4 + Framework .Net 4.0.


Hmm ... vấn đề là tôi không thực sự muốn xóa bảng gói, bởi vì nó sẽ chứa rất nhiều bảng khác từ các trang khác nhau (được tạo từ các nhóm phụ thuộc khác nhau). Nhưng vì điều này thực sự chỉ để làm việc trong môi trường phát triển, tôi nghĩ rằng tôi có thể sao chép nội dung của nó, sau đó xóa nó, sau đó thêm chúng lại, nếu điều đó sẽ xóa bộ nhớ cache. Không hiệu quả kinh khủng nhưng nếu nó hoạt động, nó đủ tốt cho nhà phát triển.
Jamie Treworgy

Đồng ý nhưng đó là lựa chọn duy nhất tôi có. Tôi đã dành cả buổi chiều để cố gắng hiểu vấn đề là gì.
LeftyX

2
Mình mới thử, VẪN không bị tuôn bộ nhớ đệm !! Tôi đã xóa nó ResetAllvà đã thử đặt EnableOptimizationsthành false cả khi khởi động và nội tuyến khi tôi cần đặt lại bộ nhớ cache, không có gì xảy ra. Argh.
Jamie Treworgy

Nó chắc chắn sẽ được tốt đẹp nếu các nhà phát triển có thể bắn ra một bài đăng blog nhanh chóng với ngay cả một one-liner về các phương pháp trong các đối tượng này :)
Jamie Treworgy

6
Vì vậy, chỉ để giải thích những gì các phương pháp này làm: Scripts.Url chỉ là một bí danh cho BundleTable.Bundles.ResolveBundleUrl, nó cũng sẽ giải quyết các url không phải gói, do đó, một trình giải quyết url chung của nó sẽ xảy ra để biết về các gói. Scripts.Render sử dụng cờ EnableOptimizations để xác định xem có hiển thị tham chiếu đến các gói hoặc các thành phần tạo nên gói hay không.
Hao Kung

8

Ghi nhớ các khuyến nghị của Hao Kung để không làm điều này vì các tình huống trang trại web, tôi nghĩ rằng có rất nhiều trường hợp mà bạn có thể muốn làm điều này. Đây là một giải pháp:

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

Bạn có thể gọi mã trên bất kỳ lúc nào và các gói của bạn sẽ được cập nhật. Điều này hoạt động cả khi EnableOptimizations là đúng hoặc sai - nói cách khác, điều này sẽ đưa ra đánh dấu chính xác trong các kịch bản gỡ lỗi hoặc trực tiếp, với:

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

Đọc thêm ở đây nói một chút về bộ nhớ đệm vàGenerateBundleResponse
Zac

4

Tôi cũng gặp phải vấn đề với việc cập nhật các gói mà không cần xây dựng lại. Dưới đây là những điều quan trọng cần hiểu:

  • Gói KHÔNG được cập nhật nếu đường dẫn tệp thay đổi.
  • Gói KHÔNG được cập nhật nếu đường dẫn ảo của gói thay đổi.
  • Gói KHÔNG được cập nhật nếu tệp trên đĩa thay đổi.

Vì vậy, biết rằng, nếu bạn đang thực hiện gói động, bạn có thể viết một số mã để làm cho đường dẫn ảo của gói dựa trên đường dẫn tệp. Tôi khuyên bạn nên băm các đường dẫn tệp và nối băm đó vào cuối đường dẫn ảo của gói. Bằng cách này khi đường dẫn tệp thay đổi thì đường dẫn ảo và gói sẽ cập nhật.

Đây là mã mà tôi đã kết thúc với nó đã giải quyết được vấn đề cho tôi:

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

Nói chung, tôi khuyên bạn nên tránh Aggregateviệc nối chuỗi, do nguy cơ ai đó không nghĩ đến thuật toán Schlemiel the Painter vốn có khi sử dụng nhiều lần +. Thay vào đó, chỉ cần làm string.Join("", filePaths). Điều này sẽ không có vấn đề đó, ngay cả đối với đầu vào rất lớn.
ErikE

3

Bạn đã thử lấy từ ( StyleBundle hoặc ScriptBundle ), thêm không có bao gồm trong hàm tạo của bạn và sau đó ghi đè

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

Tôi làm điều này cho các biểu định kiểu động và EnumerateFiles được gọi theo mọi yêu cầu. Nó có lẽ không phải là giải pháp tốt nhất nhưng nó hoạt động.


0

Xin lỗi để khôi phục một chuỗi đã chết, tuy nhiên, tôi đã gặp phải sự cố tương tự với bộ nhớ đệm Gói trong một trang web Umbraco nơi tôi muốn các bảng định kiểu / tập lệnh tự động thu nhỏ khi người dùng thay đổi phiên bản đẹp trong phần phụ trợ.

Mã tôi đã có (trong phương thức onSaved cho biểu định kiểu):

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

và (onApplicationStarted):

BundleTable.EnableOptimizations = true;

Bất kể tôi đã thử gì, tệp "~ / Bundles / styles.min.css" dường như không thay đổi. Trong phần đầu trang của tôi, lúc đầu tôi đang tải trong biểu định kiểu như sau:

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

Tuy nhiên, tôi đã làm cho nó hoạt động bằng cách thay đổi điều này thành:

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

Phương thức Styles.Render kéo vào một chuỗi truy vấn ở cuối tên tệp mà tôi đoán là khóa bộ nhớ cache được Hao mô tả ở trên.

Đối với tôi, nó chỉ đơn giản như vậy. Hy vọng điều này sẽ giúp bất kỳ ai khác giống như tôi, những người đã tìm kiếm điều này trong nhiều giờ và chỉ có thể tìm thấy các bài đăng cũ vài năm!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.