Click here to Skip to main content
15,878,809 members
Articles / Web Development / ASP.NET

Bundling and Minification (ASP.NET 4.0)

Rate me:
Please Sign up or sign in to vote.
4.89/5 (14 votes)
28 Jul 2014CPOL6 min read 82.9K   2.2K   29   20
Implementing Bundling and Minification in a manageable way

Introduction 

I know it's a little late to talk about ASP.NET 4.0, but I think its really important to put this idea of bundling and minification. I hope everyone today knows the importance of minimizing page resources and increasing the page performance. This article does not talk about why Bundling and Minification is important but talks about how well we can implement that in ASP.NET 4.0.

Bundling and Minification is really good and important for the site performance. We need to find a way to make it so easy to manage so that we would not require to do the Bundling and minification manually. Once defined, our code should take care of these process. 

The code sample that I have provided in this article is 1 year old (with few modifications). I implemented this way of managing the Bundling and Minification in one of the projects and thought I should share this idea.  

Steps to implement Bundling and Minification  

Following are the base points to make Bundling and Minification manageable:

  1. Adding Microsoft ASP.NET Web Optimization Framework: This framework is used to create minified version along with the specified bundles of the files. Download and add file reference to your project from here.
  2. Create an XML file to define bundling of the files. You can define two bundles to each page, one is for the common files bundle which is required on each page. Second will be the bundle for page specific files. Page specific files may include page JavaScript and all the controls JavaScript that are added to the page.
  3. Once the bundles are defined in XML file, you can add the bundling code in global.asax page. This code will take care of creating Bundles and Minification of the files.
  4. Adding bundle reference to a page. If the debug mode is false, then only the Bundle file should get added to the page. Otherwise, each individual file with normal version of the file (not minified) should get added to the page. This makes developer convenient to debug the JavaScript code.

See it in action

Let's implement all steps one by one:

1.   Adding Microsoft ASP.NET Web Optimization Framework

Go to http://www.nuget.org/packages/microsoft.aspnet.web.optimization/

This will instruct you for how to add a reference of System.Web.Optimization.dll to your project. This class take cares the actual Bundling and Minifications.

2.   Create an XML to add bundling definitions as follows:

HTML
<Bundling>
  <Js Name="~/AdminJS.js">
    <Path>~/scripts/jquery-1.9.0.min.js</Path>
    <Path>~/scripts/AppCommon.js</Path>
    <Path>~/scripts/jqGrid/jquery.jqGrid.min.js</Path>
    <Path>~/Scripts/jqGrid/grid.locale-en.js</Path>
  </Js>
  <Css Name="~/AdminCss.css">
    <Path>~/Styles/Site.css</Path>
  </Css>
</Bundling>  

XML above defined main Bundle and its child files. In the same way, we can use for creating CSS Bundling.

You may have to define Js, Css and Bundling classes before adding further code. This will allow you to serialize the XML for reading Bundling file details from XML file. Here are those classes:

C#
[Serializable]
public class Js
{
    [XmlAttribute]
    public string Name { get; set; }
    [XmlElement]
    public string[] Path { get; set; }
}
public class Css
{
    [XmlAttribute]
    public string Name { get; set; }
    [XmlElement]
    public string[] Path { get; set; }
}
[Serializable]
public class Bundling
{
    [XmlElement]
    public Js[] Js { get; set; }
    [XmlElement]
    public Css[] Css { get; set; }
} 

3.   Register Bundling information

We have defined the JavaScript files and their Bundle names. Let's see how we can read the Bundle information and add it to memory.

C#
public void AddBundling()
{
    string filePath = String.Empty;
    string bundleName = String.Empty;
    try
    {
        StreamReader reader = new StreamReader(HttpContext.Current.Server.MapPath
              ("~/Settings/FileBundling.xml"));

        XmlSerializer serializer = new XmlSerializer(typeof(Bundling));

        Bundling bundlingInfo = (Bundling)serializer.Deserialize(reader);

        reader.Close();
        reader.Dispose();

        #region Bundling of the css files.

        Bundle cssBundle;

        foreach (Css css in bundlingInfo.Css)
        {
            cssBundle = new Bundle(css.Name);
            foreach (string cssFile in css.Path)
            {
                cssBundle.Include(cssFile);
            }
            BundleTable.Bundles.Add(cssBundle);
        }

        #endregion

        #region Bundling of the java script files.

        Bundle jsBundle;

        foreach (Js js in bundlingInfo.Js)
        {
            bundleName = js.Name;
            jsBundle = new Bundle(js.Name);

            foreach (string jsFile in js.Path)
            {
                filePath = jsFile;
                jsBundle.Include(jsFile);
            }

            BundleTable.Bundles.Add(jsBundle);
        }

        #endregion
    }
    catch (Exception ex)
    {
        throw new Exception("There is a problem while creating Bundle", ex);
    }
} 

The above code adds the Bundles to the application memory. To allow adding these Bundles adding/creating, we will prefer to call those on Application Start event. Here is the code:

C#
void Application_Start(object sender, EventArgs e)
{
    ApplicationSettingsHelper appSettings = new ApplicationSettingsHelper();
    appSettings.AddBundling();
} 

Till this point, we have created the Bundle definition in XML file. According to the Bundle definitions, we added the Bundle to the memory on Application Start event.

Here is the generic code that allows us to add the script file reference to page:

C#
public static void AddScriptFromBundle(string bundleName, Page varThis, string scriptKey)
{
    StringBuilder strScript = new StringBuilder();
    if (HttpContext.Current.IsDebuggingEnabled)
    {
        StreamReader reader = new StreamReader(HttpContext.Current.Server.MapPath
                 ("~/Settings/FileBundling.xml"));
        XmlSerializer serializer = new XmlSerializer(typeof(Bundling));
        Bundling bundlingInfo = (Bundling)serializer.Deserialize(reader);
        reader.Close();
        reader.Dispose();
        foreach (Js js in bundlingInfo.Js)
        {
            if (js.Name.Trim('~') == bundleName)
            {
                foreach (string jsFile in js.Path)
                {
                    strScript.Append(String.Format("<script src=\"{0}?v=" + 
                           ConfigurationManager.AppSettings["ScriptVersion"] + 
                           "\" type=\"text/javascript\">
					</script>", jsFile.Trim('~')));
                }
                break;
            }
        }
    }
    else
    {
        strScript.Append(String.Format("<script src=\"{0}?v=" + 
          ConfigurationManager.AppSettings["ScriptVersion"] + 
				"\" type=\"text/javascript\"></script>", 
          bundleName));
    }
    varThis.ClientScript.RegisterStartupScript(varThis.GetType(), scriptKey, strScript.ToString());
} 

Now why is this code required to add just a one JavaScript reference to the page?

The following points provide the answer:

  • We want to add a script reference at the end of the page not in between the page or user control.
  • Bundle should only get added to the page if the debug mode is false. If the debug mode is true, we want to add every individual JavaScript file to the page. This will allow us to get the clean and normal JavaScript file which we can debug easily.
  • We want to add a versioning to the Bundles of the individual files that are getting added to the page. 

4. Adding Bundle references to the page 

Now, we have all Javascript Bundles ready to add to the page. There are number of ways to add these references to the page. All have approaches have own advantages and disadvantages.

Approach1

Add a simple Javascript Bundle reference to a aspx page. Like we add any javascript reference, we can add Bundle file reference to the page directly.

<script language="javascript" type="text/javascript" src="/AdminJS.js"></script>

Problem with approach is, it will not add and versioning information for the Bundle.

Approach 2

We can add reference to the page using Scripts.Render method. This methods solves two problems. One is to add Bundle reference to the page and secondly adds versioning information to the page. These versioning information will be managed by the Scripts.Render itself. So every time you change the javascript, Bundle will refresh itself along with the verioning information. It will be application for recycling the app pool. 

<%:System.Web.Optimization.Scripts.Render("~/AdminJS.js")%>

Ploblem with approach 2 is, it will add reference to the page where we add this line. Prefereable approach will be, add files to the bottom of the page instead of inbetween. 

Approach 3

Below is the approach to inject javascript reference to the page through code. This is a big advantage when you want to bind Javascript reference to the user control and not with the aspx page. So if you add/move user control, your controls Javascript file will move along with the user control. 

There is one more line Page.ClientScript.IsClientScriptIncludeRegistered. This line makes sure, if you add User Control twice to page, Javascrpt will not get add twice but just just once. 

#region Constants

private const string JAVA_SCRIPT_CLASS_KEY = "AdminHomePage";
private const string CONTROL_KEY = "ControlKey";
private const string SCRIPT_PATH = "~/AdminJS.js";

#endregion

protected void Page_Load(object sender, EventArgs e)
{
    RegisterClientClass();
}

/// <summary>
/// Register javascript class file. Also create a javascript class object.
/// </summary>
private void RegisterClientClass()
{
    if (!Page.ClientScript.IsClientScriptIncludeRegistered(CONTROL_KEY))
    {
        Page.ClientScript.RegisterStartupScript(typeof(Page), CONTROL_KEY, Scripts.Render(SCRIPT_PATH).ToHtmlString());
    }

    Dictionary<string, string> jsControls = new Dictionary<string, string>();
    jsControls.Add(divBannerContainer.ID, divBannerContainer.ClientID);

    WebHelper.RegisterClientScriptClass(this.ClientID, this.Page, JAVA_SCRIPT_CLASS_KEY, jsControls);
}

Arroach 3 will be the best way to add reference to the page but there is one disadvantage comes with that approach. if I add a custom Javasript to the page it should have version on App Pool recycle and on change in file. But there are files like jQuery plugins, these do not get frequently change over the time. So I would not prefere to add new version information every time I Recycle App Pool. To avoid this problem we can add one custom code to maintain version information from web.config file as mentioned in below approach. 

Approach 4

Below is code for replacement of the Page.ClientScript.IsClientScriptIncludeRegistered method. AddScriptFromBundle method manages versioning information from web.config file.

​  <% ApplicationSettingsHelper.AddScriptFromBundle("/AdminJS.js", this, "DefaultPage"); %> 

Based on the requirements, make your own choice on which approach to implement. 

Points of Interest

Bundling and Minification is always good for the page performance. The above concept really helps to keep us away from worrying about the Bundling and Minification of the JavaScript files.

Also, it is easy to manage the XML to maintain the Bundles for the pages.

I hope this code and concept will help to improve the framework design for projects. I know there is always a scope for improvement and writing good code. Any comments and suggestions will be appreciated.  

Thanks.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Perficient Inc.
India India
When ever I do write a code or provide a solution, I do provide it with the feeling that this is the best way of doing it. After few day I find a new idea of doing the same thing with better way and I correct the previous solution. After few more days I again get a better solution for the same thing... and this is not stopping, I don't know what to do about this... Smile | :)

Comments and Discussions

 
QuestionWhy some one follow your path Pin
Mou_kol6-Jun-17 22:37
Mou_kol6-Jun-17 22:37 
Questionis this bundling framework required for bundling and minification Pin
syedkhaleel27-Nov-14 20:52
syedkhaleel27-Nov-14 20:52 
AnswerRe: is this bundling framework required for bundling and minification Pin
Kapil Khadgi1-Dec-14 14:32
professionalKapil Khadgi1-Dec-14 14:32 
QuestionBundling but not minification? Pin
Muhammad Aqib Shehzad25-Nov-14 4:11
professionalMuhammad Aqib Shehzad25-Nov-14 4:11 
AnswerRe: Bundling but not minification? Pin
Kapil Khadgi1-Dec-14 14:36
professionalKapil Khadgi1-Dec-14 14:36 
GeneralRe: Bundling but not minification? Pin
Member 1214030415-Nov-15 0:18
Member 1214030415-Nov-15 0:18 
QuestionThis does seem a lot like my method Pin
James Curran28-Jul-14 9:21
James Curran28-Jul-14 9:21 
JavascriptHelper:Managing JS files for ASP.NET MVC (With Bundling)[^]
Truth,
James

AnswerRe: This does seem a lot like my method Pin
Kapil Khadgi5-Aug-14 21:44
professionalKapil Khadgi5-Aug-14 21:44 
QuestionQuestion Pin
DarkElieDraven13-Feb-14 2:31
DarkElieDraven13-Feb-14 2:31 
AnswerRe: Question Pin
Kapil Khadgi28-Jul-14 8:32
professionalKapil Khadgi28-Jul-14 8:32 
QuestionSome of my thoughts Pin
itltf51211610-Dec-13 17:40
itltf51211610-Dec-13 17:40 
AnswerRe: Some of my thoughts Pin
Kapil Khadgi10-Dec-13 19:26
professionalKapil Khadgi10-Dec-13 19:26 
Question404 Error for js file Pin
Member 104561129-Dec-13 23:51
Member 104561129-Dec-13 23:51 
QuestionNot an article... Pin
Dave Kreskowiak6-Dec-13 3:24
mveDave Kreskowiak6-Dec-13 3:24 
AnswerRe: Not an article... Pin
JMummery7-Aug-15 3:36
professionalJMummery7-Aug-15 3:36 
GeneralRe: Not an article... Pin
Dave Kreskowiak7-Aug-15 12:38
mveDave Kreskowiak7-Aug-15 12:38 
GeneralRe: Not an article... Pin
Kapil Khadgi6-Jan-16 8:35
professionalKapil Khadgi6-Jan-16 8:35 
GeneralRe: Not an article... Pin
Dave Kreskowiak6-Jan-16 9:18
mveDave Kreskowiak6-Jan-16 9:18 
GeneralRe: Not an article... Pin
Kapil Khadgi6-Jan-16 17:40
professionalKapil Khadgi6-Jan-16 17:40 
GeneralRe: Not an article... Pin
Dave Kreskowiak7-Jan-16 3:36
mveDave Kreskowiak7-Jan-16 3:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.