Gunnar Peipman's ASP.NET blog

ASP.NET, C#, SharePoint, SQL Server and general software development topics.

Sponsors

News

 
 
 
 
 
Programming Blogs - Blog Catalog Blog Directory
 
 
 

Links

Social

ASP.NET MVC: How to combine scripts and other resources

ASP.NET pages that use AJAX components make usually many requests to server per one page to load all required JavaScript and CSS files. Connections, like all other real time resources, are most expensive to create and keep. If we can somehow decrease the number of requests per page load then we need less server resources for same amount of users. For ASP.NET forms we can use script combining, for ASP.NET MVC applications we can use ASP.NET MVC Client-side Resource Combine.

MVC Resource Combine is open-source project you can find in CodePlex. ASP.NET official ScriptManager needs server-side form as a container. This means that all scripts are loaded in the body of page. I have found no way how to make ScriptManager to create script and other references to page header. MVC Resource Combine is free of this problem.

MVC Resource Combine is easy to use. Just follow these steps.

  1. Download the latest release and add references to DLL-s of MVC Resource Combine.
  2. Add the following line to web.config file, under <configSections> element:

    <section name="resourceCombine" type="Mvc.ResourceCombine.ConfigSectionSetting, 
    Mvc.ResourceCombine
    " />


  3. Add the following line to web.config file, under <configuration> section:

    <resourceCombine definitionUrl="~/App_Data/combine.xml" />

      
  4. Now open Global.asax and add the following line before all the other route definitions:
    routes.AddResourceCombineRoute("Resource Combine");

     
  5. To App_Data folder add new XML-file combine.xml. By example, for jqGrid you may have JavaScript resource set like this:

    <?xml version="1.0" encoding="utf-8" ?>
    <resourceCombine url="~/combine.axd" defaultDuration="15" 
    defaultVersion="1" >
      <resourceSet name="jqGrid" type="js" duration="30" 
    version="a">
        <resource path="~/Scripts/jquery-1.3.2.js" 
    mode="LocalStatic" />
        <resource path="~/Scripts/jquery.jqGrid.js" 
    mode="LocalStatic" />
        <resource path="~/Scripts/js/jqModal.js" 
    mode="LocalStatic" />
        <resource path="~/Scripts/js/jqDnR.js" 
    mode="LocalStatic" />
        <resource path="~/Scripts/jquery.ajaxQueue.js" 
    mode="LocalStatic" />
        <resource path="~/Scripts/jquery.bgiframe.min.js" 
    mode="LocalStatic" />
        <resource path="~/Scripts/thickbox-compressed.js" 
    mode="LocalStatic" />
        <resource path="~/Scripts/jquery.autocomplete.js" 
    mode="LocalStatic" />
      </resourceSet>
    </resourceCombine>

      
  6. Now open Site.Master and add call for MVC Resource Combine and specify resource set by its name (jqGrid):  

    <%= Html.CombinerLink("jqGrid")%>

      
  7. Now run your application and view page source. You should see something like this:
    <script type="text/javascript" src="/combine.axd/jqGrid/a">
    </
    script>

NB! Don’t forget to add reference to Mvc.ResourceCombine namespace in your Global.asax and Site.Master files. Well, if you have Resharper, then it is almost possible to forget these references. :)

Now, let’s see results. It’s Saturday and I’m watching Estonian biggest song contest, so I’m too lazy to add Firebug screenshots here. I have a little bit different configuration but numbers in the following table should give you some idea about wins in performance.

  Before
combine
After
combine
Difference
(before/after)
Requests 21 4 5.25
Size (kb) 259 19 13.63
Time (s) 7.69 4.84 1.59

As you can see, using MVC Resource Combining it is possible to achieve better performance with pretty simple and time consuming efforts. Also you don’t have to mix ScriptManager and other ASP.NET forms elements to your ASP.NET MVC views. By the way, you can combine also all the other resources that can be downloaded as one file – by example, style sheets.


kick it on DotNetKicks.com vote it on WebDevVote.com pimp it Progg it Shout it

Comments

DotNetShoutout said:

Thank you for submitting this cool story - Trackback from DotNetShoutout

# July 4, 2009 3:58 PM

PimpThisBlog.com said:

Thank you for submitting this cool story - Trackback from PimpThisBlog.com

# July 4, 2009 3:59 PM

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# July 4, 2009 4:01 PM

WebDevVote.com said:

You are voted (great) - Trackback from WebDevVote.com

# July 4, 2009 4:03 PM

progg.ru said:

Thank you for submitting this cool story - Trackback from progg.ru

# July 4, 2009 4:07 PM

seodigg.org said:

ASP.NET pages that use AJAX components make usually many requests to server per one page to load all required JavaScript and CSS files. Connections, like all other real time resources, are most expensive to create and keep. If we can somehow decrease

# July 4, 2009 4:09 PM

News ASP.NET MVC: How to combine scripts and other resources | Web 2.0 Designer said:

Pingback from  News   ASP.NET MVC: How to combine scripts and other resources | Web 2.0 Designer

# July 4, 2009 8:42 PM

The Technology Post for July 6th, 2009 | I love .NET! said:

Pingback from  The Technology Post for July 6th, 2009 | I love .NET!

# July 6, 2009 9:03 PM

Sanjeev Agarwal said:

Daily tech links for .net and related technologies - July 5-8, 2009 Web Development The MVC in JavaScriptMVC

# July 7, 2009 5:49 AM

The Technology Post for July 6th, 2009 | rapid-DEV.net said:

Pingback from  The Technology Post for July 6th, 2009 | rapid-DEV.net

# July 7, 2009 7:46 AM

Webmaster Crap » Blog Archive » ASP.NET MVC: How to combine scripts and other resources - Gunnar … said:

Pingback from  Webmaster Crap  &raquo; Blog Archive   &raquo; ASP.NET MVC: How to combine scripts and other resources - Gunnar &#8230;

# July 12, 2009 9:27 PM

ASP.NET MVC Archived Blog Posts, Page 1 said:

Pingback from  ASP.NET MVC Archived Blog Posts, Page 1

# July 16, 2009 1:12 AM

Steve O said:

Any idea how to incorporate this into a unit testing framework? I've only just started using NUnit, Moq etc., and all of my inbound and outbound routing tests now fail with

RouteTable.Routes.AddResourceCombineRoute("Resource Combine");

in global.asax.cs

I'm not sure what I need to do in my unit tests to mock this facility to make sure my tests pass again.

# July 17, 2009 9:49 AM

DigiMortal said:

Can you paste here the error message and error trace?

# July 17, 2009 9:58 AM

Steve O said:

In NUnit, one error is:

Tests.InboundRoutingTests._2_Goes_To_Headlines_Page_2:

System.ArgumentNullException : Value cannot be null.

Parameter name: context

at Mvc.ResourceCombine.ConfigUtils.GetSettings(HttpContext context)

at Mvc.ResourceCombine.WebExtensions.AddResourceCombineRoute(RouteCollection routes, String name)

at GPv3mvc.MvcApplication.RegisterRoutes(RouteCollection routes) in C:\Documents and Settings\Steve.LAPPY\My Documents\Source code\C#\GPv3mvcSolution\GPv3mvc\Global.asax.cs:line 19

at Tests.InboundRoutingTests.TestRoute(String url, Object expectedValues) in C:\Documents and Settings\Steve.LAPPY\My Documents\Source code\C#\GPv3mvcSolution\Tests\InboundRoutingTests.cs:line 221

at Tests.InboundRoutingTests._2_Goes_To_Headlines_Page_2() in C:\Documents and Settings\Steve.LAPPY\My Documents\Source code\C#\GPv3mvcSolution\Tests\InboundRoutingTests.cs:line 30

This is the test:

[Test]

public void _2_Goes_To_Headlines_Page_2()

{

TestRoute("~/2", new

{

controller = "Headlines",

action = "Index",

formatFilter = "",

pageNumber = 2

});

}

(supporting function)

/// <summary>

/// Tests the route.

/// </summary>

/// <param name="url">The URL.</param>

/// <param name="expectedValues">The expected values.</param>

private void TestRoute(string url, object expectedValues)

{

// Arrange: Prepare the route collection and a mock request context

RouteCollection routes = new RouteCollection();

MvcApplication.RegisterRoutes(routes);

var mockHttpContext = new Moq.Mock<HttpContextBase>();

var mockRequest = new Moq.Mock<HttpRequestBase>();

mockHttpContext.Setup(x => x.Request).Returns(mockRequest.Object);

mockRequest.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns(url);

// Act: Get the mapped route

RouteData routeData = routes.GetRouteData(mockHttpContext.Object);

// Assert: Test the route values against expectations

Assert.IsNotNull(routeData);

var expectedDict = new RouteValueDictionary(expectedValues);

foreach (var expectedVal in expectedDict)

{

if (expectedVal.Value == null)

Assert.IsNull(routeData.Values[expectedVal.Key]);

else

Assert.AreEqual(expectedVal.Value.ToString(),

routeData.Values[expectedVal.Key].ToString());

}

}

Line 19 in global.asax.cs is the first line inside RegisterRoutes:

RouteTable.Routes.AddResourceCombineRoute("Resource Combine");

# July 17, 2009 10:35 AM

DigiMortal said:

It seems like something wants to detect current HttpContext.

// You have to create mock for this one because you

// cannot instantiate it (abstract)

var req = new HttpWorkerRequest();

var con = new HttpContext(req);

System.Web.HttpContext.Current = con;

If there are more problems to be fixed then please let me know.

# July 17, 2009 1:15 PM

Steve O said:

I take it this is supposed to go in TestRoute, but the line:

var req = new HttpWorkerRequest();

results in the error:

"Cannot create an instance of the abstract class or interface 'System.Web.HttpWorkerRequest'"

Thanks for your help so far. I'm so confused by Moq and Unit Testing!

# July 18, 2009 11:33 AM

DigiMortal said:

Yes, you cannot create HttpWorkerRequest. Create a mock object for this and use it. Mock object should do the work.

You can also create fake HttpWorkerRequest (it's a lot more work for you). In this case you have to create a class that extends HttpWorkerRequest and implements all abstract methods and properties.

Third option is to turn off resource combining for tests. It should be the easiest thing you can do because in the tests you don't need JavaScript and other client side resources.

# July 18, 2009 2:40 PM

Steve O said:

Thanks so much for your help so far. I hate to give up on something but I think that turning it off for unit testing sounds the easiest! But how do I do that? The error is happening in global.asax.cs, which is presumably being compiled into the Web site dll, which the unit testing framework is using. How can I get global.asax.cs to ignore the line that sets up the script combiner? How do I detect that I don't need to call:

RouteTable.Routes.AddResourceCombineRoute("Resource Combine");

# July 20, 2009 1:06 PM

DigiMortal said:

I suppose you run tests using Test configuration (you can set it from Visual Studio IDE). In this case preprocessor directive #DEBUG will be defined. You can do something like this in your code:

#if !DEBUG

RouteTable.Routes.AddResourceCombineRoute("Resource Combine");

#endif

Be warned that in this case resource combining is turned off also when you run your application in debug mode (you are not testing it but running it on web server). You can also define test configuration and define TEST constant. Then you can make check like this:

#if !TEST

RouteTable.Routes.AddResourceCombineRoute("Resource Combine");

#endif

# July 24, 2009 5:56 PM

ASP.NET MVC: How to combine scripts and other resources – Gunnar … «work4real.net said:

Pingback from  ASP.NET MVC: How to combine scripts and other resources &#8211; Gunnar &#8230; &laquo;work4real.net

# September 16, 2009 6:30 AM

Yucel said:

Hi, when i add only jquery.js as resource in combine.xml and add jquery ui to mypage as default (<script src="jqueryui.js"....) it works well.

But if i add  two js files as resources in combine.xml website give me javascript error that object not found, which means that it cant find the jqueryui methods. Can you help me about that

# October 12, 2009 8:10 AM

currentspulledmeunder said:

I keep getting the error: Unrecognized configuration section resourceCombine.

Also, I don't see the file combine.axd in the download package, is this something I need to have in my project?

# October 14, 2009 2:30 AM

Tran Chi Khanh said:

Please help me. when i use this for my css, don't image show on in my page. What's happen?

<?xml version="1.0" encoding="utf-8" ?>

<resourceCombine url="~/combine.axd" defaultDuration="15"

 defaultVersion="1">

 <resourceSet name="cssAdminPage" type="css" duration="30" version="1" cacheDisabled="true">

   <resource path="~/Content/Admin/css/reset.css" />

   <resource path="~/Content/Admin/css/style.css" />

   <resource path="~/Content/Admin/css/invalid.css" />

 </resourceSet>

</resourceCombine>

# October 28, 2009 12:36 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)