Search code examples
asp.net-mvcsystem.web.optimizationbundletransformer

Bundling for Multi-tenant ASP MVC Application


I am developing a multi-tenant application with ASP MVC. The tenant is identified by subdomain. I am using System.Web.Optimization for bundling and minification and BundleTransformer.Less.

The UI can be themed by applying a specific set of LESS variables for each tenant. These variables are stored in a DB.

As per suggestion of https://stackoverflow.com/users/1292362/andrey-taritsyn, I implemented a custom VirtualPathProvider to inject the less variables in the less files that requires them. That works like a charm!

However I have some doubts regarding how bundling and caching are managed.

  1. Should I grab every possible tenant and register a bundle for each one at App_Start? (I don't like this option as I should be registering bundles that may never or occasionaly be used and I should also resolve the problem of registering new bundles for new tenants without restarting).

  2. Should I watch for every request, check if the bundle for the tenant exists, and in case it doesn't, register it?

  3. The optimal solution would be to declare the bundle only once at App_Start, and have the bundle response created and stored in cache when the first request to the tenant's subdomain comes in.

I currently have option 3 implemented and it does works, but I don't know what's really going on inside.

  • How do I customize the cache key to identify bundle AND tenant? So far, I did this:

    public class SkinnableBundle : Bundle
    {
        public SkinnableBundle(string virtualPath)
            : this(virtualPath, null)
        { }
    
        public SkinnableBundle(string virtualPath, string cdnPath)
            : base(virtualPath, cdnPath,
                new IBundleTransform[] { BundleTransformerContext.Current.Css.GetTransformerInstance() })
        {
            Builder = new NullBuilder();
        }
    
        public override string GetCacheKey(BundleContext context)
        {
            if (context.HttpContext == null)
            {
                return base.GetCacheKey(context);
            }
    
            var host = context.HttpContext.Request.Url.Host.ToLowerInvariant();
            return string.Format("System.Web.Optimization.Bundle:{0}:{1}", host, context.BundleVirtualPath);
        }
     }
    
    • A bundle is identified by the same virtual path, but: Are many bundle responses being cached (tenant-specific)? Or there is only one bundle response that is being overwritten?

    • How do I retrieve the tenant-specific bundle response at the layout? What happens when I do @Styles.Render("~/bundles/css/common")? Is it identifying the bundle response based on the virtual path and tenant?


Solution

  • I am almost certain that multiple tenant-specific bundle response are being stored on the cache. I installed Glimpse and inspected the cache: I found that one entry per tenant for the bundle is being listed.