[c#] BundleCollection이 MVC4에서 캐시 된 스크립트 번들을 플러시하도록하는 방법

… 또는 내가 어떻게 걱정을 멈추고 마이크로 소프트의 완전히 문서화되지 않은 API에 대해 코드를 작성하는 법을 배웠습니다 . 공식 System.Web.Optimization릴리스에 대한 실제 문서가 있습니까? ‘Cuz 나는 확실히 찾을 수없고, XML 문서도없고, 모든 블로그 게시물은 상당히 다른 RC API를 참조합니다. 아니 후 ..

자바 스크립트 종속성을 자동으로 해결하는 코드를 작성 중이며 해당 종속성에서 즉시 번들을 생성하고 있습니다. 스크립트를 편집하거나 응용 프로그램을 다시 시작하지 않고 번들에 영향을주는 변경 사항을 적용하는 경우를 제외하고 모든 것이 잘 작동합니다. 변경 사항이 반영되지 않습니다. 그래서 개발에 사용할 종속성 캐싱을 비활성화하는 옵션을 추가했습니다.

그러나 번들 컬렉션이 변경된 경우에도 분명히 BundleTablesURL을 캐시합니다 . 예를 들어 번들을 다시 만들고 싶을 때 내 코드에서 다음과 같이합니다.

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

별칭이 같은 번들 제거하고 다시 만들 때마다 아무 일도 일어나지 않습니다. bundleUrl반환 된 항목 ResolveBundleUrl은 번들을 제거하고 다시 생성하기 전과 동일합니다. “동일”이란 번들의 새 콘텐츠를 반영하기 위해 콘텐츠 해시가 변경되지 않음을 의미합니다.

편집 … 사실, 그것은 그것보다 훨씬 더 나쁩니다. 번들 자체가 어떻게 든 외부의 캐시 Bundles모음입니다. 브라우저가 스크립트를 캐싱하지 못하도록 내 임의의 해시를 생성하면 ASP.NET은 이전 스크립트를 반환 합니다 . 따라서 분명히 번들을 제거하는 BundleTable.Bundles것은 실제로 아무것도하지 않습니다.

이 문제를 해결하기 위해 별칭을 간단히 변경할 수 있으며 개발에는 문제가 없지만 페이지가로드 될 때마다 별칭을 폐기해야하거나 크기가 커지는 BundleCollection이 있어야하기 때문에 그 아이디어가 마음에 들지 않습니다. 모든 페이지로드. 프로덕션 환경에이 기능을 그대로두면 재앙이 될 것입니다.

따라서 스크립트가 제공 될 때 실제 BundleTables.Bundles객체와 독립적으로 캐시되는 것 같습니다 . 따라서 URL을 재사용하는 경우 재사용하기 전에 참조한 번들을 제거 했더라도 캐시에있는 모든 항목으로 응답하고 Bundles객체를 변경해도 캐시가 플러시되지 않으므로 항목 만 (또는 오히려 다른 이름의 새 항목)이 사용됩니다.

동작이 이상해 보입니다. 컬렉션에서 무언가를 제거하면 캐시에서 제거해야합니다. 하지만 그렇지 않습니다. 이 캐시를 플러시하고 BundleCollection해당 번들이 처음 액세스 될 때 캐시 된 내용 대신의 현재 내용을 사용하도록하는 방법이 있어야합니다 .

내가 어떻게 할 수 있을지 아십니까?

ResetAll목적을 알 수없는 이 방법이 있지만 어차피 부수기 때문에 그렇지 않습니다.



답변

문서화에 대한 귀하의 고통을 들었습니다. 안타깝게도이 기능은 여전히 ​​매우 빠르게 변경되고 있으며 문서 생성에는 약간의 지연이 있으며 거의 ​​즉시 구식이 될 수 있습니다. Rick의 블로그 게시물 이 최신 상태이며, 그 동안 현재 정보를 전파하기 위해 여기에서 질문에 답하려고 노력했습니다. 우리는 현재 항상 최신 문서가있는 공식 codeplex 사이트를 설정하는 중입니다.

이제 캐시에서 번들을 플러시하는 방법에 대한 특정 문제와 관련하여.

  1. 요청 된 번들 URL에서 생성 된 키를 사용하여 번들 된 응답을 ASP.NET 캐시 내부에 저장합니다. 즉, Context.Cache["System.Web.Optimization.Bundle:~/bundles/jquery"]이 번들을 생성하는 데 사용 된 모든 파일 및 디렉터리에 대한 캐시 종속성도 설정합니다. 따라서 기본 파일이나 디렉토리가 변경되면 캐시 항목이 플러시됩니다.

  2. 요청별로 BundleTable / BundleCollection의 라이브 업데이트를 실제로 지원하지 않습니다. 완전히 지원되는 시나리오는 앱 시작 중에 번들이 구성된다는 것입니다 (웹 팜 시나리오에서 모든 것이 제대로 작동하므로 일부 번들 요청이 잘못된 서버로 전송되면 404가됩니다). 코드 예제를 보면 특정 요청에 대해 번들 컬렉션을 동적으로 수정하려는 것 같습니다. 모든 종류의 번들 관리 / 재구성에는 모든 것이 올바르게 설정되었는지 확인하기 위해 appdomain 재설정이 수반되어야합니다.

따라서 앱 도메인을 재활용하지 않고 번들 정의를 수정하지 마십시오. 번들 내의 실제 파일을 자유롭게 수정할 수 있으며, 자동으로 감지되어 번들 URL에 대한 새 해시 코드를 생성해야합니다.


답변

비슷한 문제가 있습니다.
내 수업에서 BundleConfig나는 BundleTable.EnableOptimizations = true.

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

        bundles.Add(...);
    }
}

모든 것이 잘 작동했습니다.
어느 시점에서 디버깅을 수행하고 속성을 false로 설정했습니다.
나는 jquery (첫 번째) 번들이 해결되지 않고로드되지 않는 것처럼 보였기 때문에 무슨 일이 일어나고 있는지 이해하는 데 어려움을 겪었습니다 ( /bundles/jquery?v=).

욕을 좀하고 나서 (?!) 정리해 낸 것 같아요. 추가하려고 bundles.Clear()하고 bundles.ResetAll()등록과 사물의 시작 부분에 다시 작업을 시작해야합니다.

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

        BundleTable.EnableOptimizations = false;

        bundles.Add(...);
    }
}

EnableOptimizations속성을 변경할 때만이 두 가지 방법을 실행해야한다는 것을 깨달았습니다 .

최신 정보:

파고 깊은 나는 것을 발견했습니다 BundleTable.Bundles.ResolveBundleUrl@Scripts.Url번들 경로를 해결하기 위해 문제를 갖고있는 것 같다.

간단하게하기 위해 몇 가지 이미지를 추가했습니다.

이미지 1

최적화를 해제하고 몇 가지 스크립트를 번들로 제공했습니다.

이미지 2

동일한 번들이 본체에 포함되어 있습니다.

이미지 3

@Scripts.Url@Scripts.Render적절한 경로를 생성하는 동안 번들의 “최적화 된”경로를 제공합니다 .
같은 일이 BundleTable.Bundles.ResolveBundleUrl.

Visual Studio 2010 + MVC 4 + Framework .Net 4.0을 사용하고 있습니다.


답변

웹 팜 시나리오로 인해이 작업을 수행하지 말라는 Hao Kung의 권장 사항을 염두에두고이를 수행하고 싶은 시나리오가 많이 있다고 생각합니다. 해결책은 다음과 같습니다.

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

언제든지 위 코드를 호출 할 수 있으며 번들이 업데이트됩니다. 이는 EnableOptimizations가 true 또는 false 일 때 모두 작동합니다. 즉, 디버그 또는 라이브 시나리오에서 올바른 마크 업이 다음과 같이 표시됩니다.

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


답변

또한 다시 빌드하지 않고 번들을 업데이트하는 데 문제가 발생했습니다. 이해해야 할 중요한 사항은 다음과 같습니다.

  • 파일 경로가 변경되면 번들이 업데이트되지 않습니다.
  • 번들의 가상 경로가 변경되면 번들이 업데이트됩니다.
  • 디스크의 파일이 변경되면 번들이 업데이트됩니다.

따라서 동적 번들링을 수행하는 경우 번들의 가상 경로가 파일 경로를 기반으로하도록 코드를 작성할 수 있습니다. 파일 경로를 해시하고 해당 해시를 번들의 가상 경로 끝에 추가하는 것이 좋습니다. 이렇게하면 파일 경로가 변경 될 때 가상 경로도 변경되고 번들이 업데이트됩니다.

이 문제를 해결 한 코드는 다음과 같습니다.

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


답변

( StyleBundle 또는 ScriptBundle ) 에서 파생 하여 생성자에 포함을 추가하지 않은 다음 재정의 해 보셨습니까?

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

동적 스타일 시트에 대해이 작업을 수행하고 EnumerateFiles는 모든 요청에 ​​대해 호출됩니다. 아마도 가장 좋은 해결책은 아니지만 작동합니다.


답변

죽은 스레드를 되살리는 것에 대해 사과했지만 Umbraco 사이트에서 번들 캐싱과 비슷한 문제가 발생했습니다. 사용자가 백엔드에서 예쁜 버전을 변경하면 스타일 시트 / 스크립트가 자동으로 축소되도록했습니다.

내가 이미 가지고있는 코드는 (스타일 시트의 onSaved 메서드에 있음) :

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

및 (onApplicationStarted) :

BundleTable.EnableOptimizations = true;

내가 무엇을 시도해도 “~ / bundles / styles.min.css”파일이 변경되지 않은 것 같습니다. 내 페이지의 머리 부분에서 나는 원래 다음과 같이 스타일 시트를로드하고있었습니다.

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

그러나 이것을 다음과 같이 변경하여 작동하게했습니다.

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

Styles.Render 메서드는 위에서 Hao가 설명한 캐시 키라고 생각하는 파일 이름 끝에 쿼리 ​​문자열을 가져옵니다.

저에게는 그렇게 간단했습니다. 이것이 몇 시간 동안 인터넷 검색을하고 몇 년 된 게시물을 찾을 수 있었던 나와 같은 다른 사람에게 도움이되기를 바랍니다!


답변