Implement the Microsoft Optimization Framework into Dynamicweb

Implement the Microsoft Optimization Framework into Dynamicweb

Introduction

This blog post is about how we can implement the Microsoft Optimization Framework within Dynamicweb and some background information about optimizing through bundling and minification.

Inside Dynamicweb we currently don't have a way to automatically minify or bundle stylesheets, javascript or any other resources. Using the Microsoft  Optimization Framework we can easily create bundles and minify stylesheets and javascript so that we can reduce the requests on the client to the webserver.

Why should we optimize?

So, first maybe some background information on why we actually should optimize our websites.

  • Significantly increase the performance of your website
    • Resources can load faster with fewer requests
    • Javascript can begin to execute sooner
  • Do not assume your users have a fast internet connection
    • Still millions of users on dial-up connections
    • Mobile connectivity can be spotty and costly for the user
  • Significantly reduce the number of requests to your server
    • Reduces your bandwith, and potentially your cost
    • Ability to support more concurrent users
  • Do not rely on the browser for caching resources
    • A good chunk of users to your public website will be consistently new
  • Development efficiencies
    • It is incredibly easy now...
    • Can save you time in development if architected correctly

Microsoft ASP.NET Optimization Library

Some facts about the Microsoft ASP.NET Optimization Library

Specifically for bundling and minifying in ASP.NET

  • It is designed for working with the .NET Framework 4.0+
  • It is open source and to be found on aspnetoptimization.codeplex.com
  • Lives inside the System.Web.Optimization namespace
  • You can get it through NuGet -> Microsoft ASP.NET Web Optimization

Key dependencies

  • Microsoft.Web.Infrastructure
    Utility to dynamically register HTTP Modules at runtime

  • WebGrease
    Suite of tools used for optimizing Javascript, CSS and images. This suite was developed by the MSN team for msn.com

Of course there are other 3rd party libraries and frameworks that you can use such as:

Optimizers

  • JSMin (JS)
  • YUICompressor (JS & CSS)
  • CSSTidy (CSS)
  • AjaxMin (JS & CSS)

Frameworks

  • SquishIt
  • ClientDependency
  • Cassette
  • Combres

We will focus on using the ASP.NET Optimization Library and we will create the bundles and transformations all through code.

Our sample solution

The sample project I'm using is a default Dynamicweb custom solution that has no templates installed, except for the default one. We should create a custom solution to be sure we can add some code to this project. Another way would be to develop a separate library that could be referenced by our Dynamicweb solution and that should be the case when you are aiming for a solution that is redistributable.

To read more about setting up a custom Dynamicweb solution, I suggest to look at the documentation or maybe use the free open source installer tool for Dynamicweb, created by Imar Spaanjaars.

Next, I downloaded a ready to use template from wrapbootstrap.com to get started with a nice design.

Installing the ASP.NET Optimization Framework

To install the ASP.NET Optimization Framework, we take the easiest approach and load the packages from Nuget. You can either use the Nuget Package manager or the console to install the "Microsoft.AspNet.Web.Optimization".

IMPORTANT
One issue I run into was that when this Nuget bundle was installed, I got an error while accessing the bundle. This was due to the WebGrease package. If you run into this error you could consider the solution that was offered here:

http://stackoverflow.com/questions/20908895/could-not-load-file-or-assembly-webgrease-one-of-its-dependencies-the-located

Examining the sample solution

So, the new default template I use has a number of stylesheets and scripts that are references. I'll focus on how to bundle and minify our scripts to one bundle. This should get you started on the idea behind bundling and minification.

Inside our index.html we have the following script references:

<!-- PLUGINS -->
<script src="assets/js/plugins/source/source/jquery.bxslider.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.centralized.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.fixedonlater.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.hashloader.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.mixitup.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.nav.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.parallax-1.1.3.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.responsivevideos.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.scrollTo-1.4.3.1.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.tweet.js" type="text/javascript"></script>
<script src="assets/js/plugins/source/jquery.tweetCarousel.js" type="text/javascript"></script>

These script files are not minified and bundled which will result in 10 requests already for this webpage to only get the script files downloaded.

Creating a bundle

We are going to create a bundle that contains all of the script files mentioned above.

1. Create our bundle class

The first thing we do is creating a folder and name it App_Start, following the default convention that Microsoft uses when creating new WebForms or MVC projects from Visual Studio. Within this folder we create a new class and name it BundleConfig.cs. This will look like this:

 

Inside this class we are going to define our bundles. You can define both bundles for the script optimization as well as bundles for stylesheet optimization. You often see this happening inside the Global.asax as well.

Creating our script bundle

Next we will create our script bundle inside our RegisterBundles method:

/// <summary>
/// Register the bundles into the bundle collection on startup
/// </summary>
/// <param name="bundles"></param>
public static void RegisterBundles(BundleCollection bundles)
{
 
   // Create a bundle that includes all script files
   bundles.Add(new ScriptBundle("~/bundles/plugins").Include(
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.bxslider.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.centralized.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.fixedonlater.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.hashloader.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.mixitup.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.nav.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.parallax-{version}.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.responsivevideos.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.scrollTo-{version}.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.tweet.js",
      "~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/jquery.tweetCarousel.js"
   ));
}

So we add a new ScriptBundle to the bundles collection that will registered into our application later. The first parameter is the VirtualPath to the bundle. This is the path the bundle will be accessible through in the browser. I used the name 'plugins' to keep it simple. After that, I used the Include method to specify the files I wanted to be included in my bundle.

You see me use the {version} notation at some files. This is one of the replacement functions available in the framework that will look up the latest version of this file.

Registering our new bundle

Now the last thing to do to get this bundle working is to register it on application start. What this means is that the RegisterBundles method should be called. This can be done in the Global.asax inside the Application_Start method.

In here we change the code to the following:

public void Application_Start(object sender, EventArgs e)
{
 
   // Register our bundle collections
   BundleConfig.RegisterBundles(BundleTable.Bundles);
 
   // Fires when the application is started
   Dynamicweb.Frontend.GlobalAsaxHandler.Application_Start(sender, e);
 
}

After we compile our project we should be able to access the bundle through the url like this:

Add the bundle to our template

Within Dynamicweb it is possible to use the default bundle and minification helpers for MVC. The page control for WebForms would not work.

Normally I would enter a line like this in ASP.NET MVC to include a reference to the bundle:

<%: Scripts.Render("~/bundles/plugins") %>

To use the MVC style we should convert our templates to Razor, which then allows us to specify the bundle like this:

@Scripts.Render("~/bundles/plugins")

For this to work, we have to add a reference to the optimization namespace on top of our template:

@using System.Web.Optimization;

I'll use this approach when talking about Dynamic Folder Bundles later.

For now let's look at the 'normal' approach within Dynamicweb when you don't want to use Razor syntax.

Since the rendering engine of Dynamicweb is a bit different than a normal ASP.NET application, we have to take a different approach. In this case I use an extender called PageTemplateExtender.

In here we can override a method called ExtendTemplate and literally extend the template with our own logic.

public class CustomPageTemplateExtender : PageTemplateExtender
{
   public override void ExtendTemplate(Dynamicweb.Rendering.Template template)
   {
      var scriptBundle = Scripts.Render("~/bundles/plugins").ToHtmlString();
      template.SetTag("FooterScripts", scriptBundle);
   }
}

So I created a new class CustomPageTemplateExtender. In here I override the ExtendTemplate method and do two things:

  1. Create a variable in which I let the Scripts.Render method render a link to my bundle. This will result in a <link /> that can be used directly in my template.
  2. Then I assing this new variable to a Dynamicweb Tag that I have to include on my template.

Inside my template I added the following tag to the markup, just before the </body> closing tag.

<!--@FooterScripts-->

Now when we compile the project and run the website, you will see the script is being rendered as follows:

<script src="/bundles/plugins?v=YHvozAsx-a22aZ93sB5Tp7fiWfkd7-t52oBb3jl2aTw1"></script>

It shows our new bundle with a time stamp for e.g. caching and version purposes.

Now when you are in debugging mode, the bundle will render the individual script files, which is good since you then are able to debug your javascript code. You can also force the optimization to be enabled always by inserting this code in for example the BundleConfig class:

BundleTable.EnableOptimizations = true;

Search for the design templates

In addition you could create bundles that will look for all files in a specific directory, which means you could search inside your current design using the Area property of the PageView and include all .css files into a new bundle.

Make it dynamic: introducing Dynamic Folder Bundles

Another approach we can make use of are Dynamic Folder Bundles, which is a feature that is mostly overlooked in configuring bundles.

  • Allows bundle transformations to be applied to folders on demand
  • Inherits from the "Bundle" class

bundles.Add(new DynamicFolderBundle("scripts", "*.js", true, new JsMinify()));

  1. The first parameter you specify is the path suffix on the url that should trigger the bundle to process.
  2. The second parameter is the search pattern and it this case it will look for all files ending with .js.
  3. The third parameter will also process files in any subdirectories.
  4. The last parameter will apply the JsMinify transformation.

So for example:

... will render all Javascript files bundled and minified in the solution

... will render all Javascript files under the /Content folder

So to make this work in our example, we will create a new DynamicFolderBundle for javascript files and add it to our BundleConfig class like this:

// Add a DynamicFolderBundle
bundles.Add(new DynamicFolderBundle("scripts", "*.js", true, new JsMinify()));

In our converted template, which now should have a .cshtml extension, we can refer to our bundle like this:

@Scripts.Render("~/Files/Templates/Designs/HelloTreble/assets/js/plugins/source/scripts")

So we now have to follow the structure of our folders and mention the path suffix we defined in our bundle.

This will then render only the .js files inside the given folder and will apply the JsMinify transformation.

Conclusion

We have looked at some ways to integrate the ASP.NET Optimization Framework into Dynamicweb. There is a lot more to say about the bundling and minification that I haven't even touched yet. You could optimize your website using bundling and minification and you really should consider either using this framework or maybe one of the 3rd party frameworks I mentioned before.

It would be easy to include other powerful functionality, for example, transformations for automatically compiling LESS or SASS files before minimizing and bundling.

If you have any questions about this topic, feel free to ping me anytime!

Credits goto Travis Gosselin for his excellent course on Pluralsight on Bundling and Minification.

door Daniel Plomp

Reactie

RadEditor - HTML WYSIWYG Editor. MS Word-like content editing experience thanks to a rich set of formatting tools, dropdowns, dialogs, system modules and built-in spell-check.
RadEditor's components - toolbar, content area, modes and modules
   
Toolbar's wrapper 
 
Content area wrapper
RadEditor's bottom area: Design, Html and Preview modes, Statistics module and resize handle.
It contains RadEditor's Modes/views (HTML, Design and Preview), Statistics and Resizer
Editor Mode buttonsStatistics moduleEditor resizer
  
RadEditor's Modules - special tools used to provide extra information such as Tag Inspector, Real Time HTML Viewer, Tag Properties and other.