How to use bundling and minification in ASP .NET

What you will learn?

ASP .NET framework provides a native and powerful scripts optimization engine that is very easy to use. In this blog post, I will go through how you can handle your website scripts in an efficient way using bundle and minification techniques. After going through this step-by-step guide - you know how to optimize website scripts properly.

Let's jump in!

Installing the Web.Optimization package

First, make sure the project includes Microsoft.AspNet.Web.Optimization package.

If the package is missing - just install the library using NuGet command:

Install-Package Microsoft.AspNet.Web.Optimization

Preparing BundleConfig inside App_Start folder

Let's create BundleConfig.cs file under App_Start folder (if the folder doesn't exist just create it manually).

ASP .NET bundling and minification BundleConfig.cs project structure

Then create the RegisterBundles method with the BundleCollection parameter inside the BundleConfig class. It is a good place to organize your scripts and group them into bundles.

public class BundleConfig
{
   public static void RegisterBundles(BundleCollection bundles)
   {
      //create bundles and add them to BundleCollection
   }
}

Creating bundles

You can create any number of bundles depending on your needs/modules (for instance home page bundles can be different than blog page bundles). It is worth experimenting and checking in practice - what combination of scripts/bundles will be most effective.

In the example below, we see two separate bundles - one for javascript files (ScriptBundle) and the second for CSS (StyleBundle). Each bundle should include relevant scripts.

Once bundles are ready, they should be added to BundleCollection as follows:

public class BundleConfig
{
   public static void RegisterBundles(BundleCollection bundles)
   {
      ScriptBundle scriptBundle = new ScriptBundle("~/bundles/master/js");

      scriptBundle
         .Include("~/assets/js/jquery.js")
         .Include("~/assets/js/plugins.js")
         .Include("~/assets/js/functions.js");

      bundles.Add(scriptBundle);

      StyleBundle styleBundle = new StyleBundle("~/bundles/master/css");

      styleBundle
         .Include("~/assets/css/plugins.css")
         .Include("~/assets/css/style.css")
         .Include("~/assets/css/custom.css");

      bundles.Add(styleBundle);

      BundleTable.EnableOptimizations = true;
   }
}

Forcing optimization

In the next step, you should disable debug mode and force optimization:

<system.web>
     <compilation debug="false" />
</system.web>
BundleTable.EnableOptimizations = true

Bundling and modification won't work without these settings.

Registering the bundling engine

Bundles should be registered when starting the application, so go to Global.asax.cs file and find Application_Start method. If it doesn't exist, just create it end execute 'BundleConfig.RegisterBundle(BundleTable.Bundles)' inside as follows:

protected void Application_Start(Object sender, EventArgs e)  
{  
    BundleConfig.RegisterBundle(BundleTable.Bundles);  
}

Bundling and minification in Razor Views:

How to use bundles in Razor Views?

First of all, make sure ~/View/Web.config file contains the new namespace 

<add namespace="System.Web.Optimization"/>

Then, include the namespace in your Razor View, so you could use the @Scripts and @Styles helpers:

@using System.Web.Optimization;

Finally, you can render the bundles in the View using helpers:

@Scripts.Render("~/bundles/master/js")
@Styles.Render("~/bundles/master/css")

Your master page can look like this:

<!DOCTYPE html>
<html lang="en-US">
<head>
    @Styles.Render("~/bundles/master/css")
</head>
<body>
    @RenderBody()

    @Scripts.Render("~/bundles/master/js")
</body>
</html>

Bonus tip: Resolving browser cache issues

It often happens that after making some scripts changes and new code release, we have unexpected feedback from the QA team/Client:

"It's not working" or "I can't see the changes".

Sounds familiar? 😀

Most of the time it's caused by the browser's caching mechanism.

How to avoid caching issues?

There is a good old trick to make sure the browser will work with the appropriate versions of scripts/bundles.

It’s really easy to force the browser cache to use the latest CSS/JS bundles by appending the query string parameter at the end of each bundle.

<link href="/bundles/master/css" rel="stylesheet"/>

Each bundle should have a unique URL after each build.

<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>

After googling here and there I found a great code snippet here on StackOverflow.

All credit goes to author Ranjeet patil (thanks)!

internal static class BundleExtensions
    {
        public static Bundle WithLastModifiedToken(this Bundle sb)
        {
            sb.Transforms.Add(new LastModifiedBundleTransform());

            return sb;
        }

        public class LastModifiedBundleTransform : IBundleTransform
        {
            public void Process(BundleContext context, BundleResponse response)
            {
                foreach (var file in response.Files)
                {
                    var lastWrite = File.GetLastWriteTime(HostingEnvironment.MapPath(file.IncludedVirtualPath)).Ticks.ToString();
                    file.IncludedVirtualPath = string.Concat(file.IncludedVirtualPath, "?v=", lastWrite);
                }
            }
        }
    }

Using this clean and awesome CSharp extension method - we get an elegant and effective code inside BundleConfig.cs:

public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            ScriptBundle scriptBundle = new ScriptBundle("~/bundles/master/js");

            scriptBundle
                .Include("~/assets/js/jquery.js")
                .Include("~/assets/js/plugins.js")
                .Include("~/assets/js/functions.js");

            bundles.Add(scriptBundle.WithLastModifiedToken());

            StyleBundle styleBundle = new StyleBundle("~/bundles/master/css");

            styleBundle
                .Include("~/assets/css/plugins.css")
                .Include("~/assets/css/style.css")
                .Include("~/assets/css/custom.css");

            bundles.Add(styleBundle.WithLastModifiedToken());

            BundleTable.EnableOptimizations = true;
        }
    }

Now, after each build - you will get a unique extra parameter in your script URL:

<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>

This trick should resolve browser caching problems and make visitors happy.

Bundles output

After going through all the steps above, you should get nice and warm bundles output:

CSS bundle output:

<link href="/bundles/master/css?v=rJwD8GQBqCoQ6sc6KrlHuWYflkjwRDAm2RK3fPv7aOk1" rel="stylesheet"/>

Js bundle output:

<script src="/bundles/master/js?v=8kmHc-ukmg3rp-jj2rNMjYYIG_lP3ErMNtowPG93XbM1"></script>

In the end, good luck to you and happy bundling & minification 😀

Comments
Leave a Comment