Search code examples
asp.net-mvc.net-4.5bundling-and-minificationasp.net-optimization

How to add bundles with dynamic content to asp.net web optimization


I'm using SignalR, which maps to asp.net application on virtual path "~/signalr". SignalR dynamically creates javascript proxy hubs on app start with virtual path "~/signalr/hubs".

So the url "[http://myapp]/signalr/hubs" is where dynamic javascript content is. How can I add this virtual path to Asp.Net Web Optimization Bundling?

Asp.Net Web Optimization Framework starting from 1.1 supports VirtuPathProvider's: ASP.NET bundling/minification: including dynamically generated Javascript

Actually I don't understand how to use these VPP's. Could somebody explain in details or better give an example?


Solution

  • Integration of dynamic content into the bundling process requires the following steps:

    1. Writing the logic that requests / builds the required content. For SignalR you could use this code snippet:

      public static string GetSignalRContent()
      {
          var resolver = new DefaultHubManager(new DefaultDependencyResolver());
          var proxy = new DefaultJavaScriptProxyGenerator(resolver, new NullJavaScriptMinifier());
          return proxy.GenerateProxy("/signalr");
      }
      
    2. Implement a virtual path provider that wraps the existing one and intercept all virtual paths that should deliver the dynamic content (just "~/signalr/hubs" in your case).

      public class CustomVirtualPathProvider : VirtualPathProvider
      {
          public CustomActionVirtualPathProvider(VirtualPathProvider virtualPathProvider)
          {
              // Wrap an existing virtual path provider
              VirtualPathProvider = virtualPathProvider;
          }
      
          protected VirtualPathProvider VirtualPathProvider { get; set; }
      
          public override string CombineVirtualPaths(string basePath, string relativePath)
          {
              return VirtualPathProvider.CombineVirtualPaths(basePath, relativePath);
          }
      
          public override bool DirectoryExists(string virtualDir)
          {
              return VirtualPathProvider.DirectoryExists(virtualDir);
          }
      
          public override bool FileExists(string virtualPath)
          {
              if (virtualPath == "~/signalr/hubs")
              {
                  return true;
              }
      
              return VirtualPathProvider.FileExists(virtualPath);
          }
      
          public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
          {
              // BaseClass can't create a CacheDependency for your content, remove it
              // You could also add your own CacheDependency and aggregate it with the base dependency
              List<string> virtualPathDependenciesCopy = virtualPathDependencies.Cast<string>().ToList();
              virtualPathDependenciesCopy.Remove("~/signalr/hubs");
      
              return VirtualPathProvider.GetCacheDependency(virtualPath, virtualPathDependenciesCopy, utcStart);
          }
      
          public override string GetCacheKey(string virtualPath)
          {
              return VirtualPathProvider.GetCacheKey(virtualPath);
          }
      
          public override VirtualDirectory GetDirectory(string virtualDir)
          {
              return VirtualPathProvider.GetDirectory(virtualDir);
          }
      
          public override VirtualFile GetFile(string virtualPath)
          {
              if (virtualPath == "~/signalr/hubs")
              {
                  return new CustomVirtualFile(virtualPath,
                      new MemoryStream(Encoding.Default.GetBytes(GetSignalRContent())));
              }
      
              return VirtualPathProvider.GetFile(virtualPath);
          }
      
          public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
          {
              return VirtualPathProvider.GetFileHash(virtualPath, virtualPathDependencies);
          }
      
          public override object InitializeLifetimeService()
          {
              return VirtualPathProvider.InitializeLifetimeService();
          }
      }
      
      public class CustomVirtualFile : VirtualFile
      {
          public CustomVirtualFile (string virtualPath, Stream stream)
              : base(virtualPath)
          {
              Stream = stream;
          }
      
          public Stream Stream { get; private set; }
      
          public override Stream Open()
          {
               return Stream;
          }
      }
      
    3. Register your virtual path provider:

      public static void RegisterBundles(BundleCollection bundles)
      {
          // Set the virtual path provider
          BundleTable.VirtualPathProvider =
              new CustomVirtualPathProvider(BundleTable.VirtualPathProvider);
      
          Bundle include = new Bundle("~/bundle")
              .Include("~/Content/static.js")
              .Include("~/signalr/hubs");
      
          bundles.Add(include);
      }
      

    For some samples of virtual path providers + bundling, see Bundling and Minification and Embedded Resources or Bundling Dynamic Generated Controller / Action Content for example.