Header Ads

Essential guide to ASP.NET MVC3 performance

The .NET CLR can give your web application a significant performance boost compared to other platforms such as PHP due to it's compiled nature. Not only does your .NET code perform better because it's more low-level, it also comes with good support for parallel programming. This guide will not explain the basics of a responsive and fast web application by talking about CSS minifying, sprites, content delivery networks, etc. Instead this guide will be an essential guide for making sure you don't miss any features that can enhance the performance of your ASP.NET MVC3 application.

Output caching

Perhaps the most useful feature of MVC3 (performance-wise) is output caching. The biggest performance hits actually occur when your application really has to fetch data, do calculations on it and return the data. Output caching can cache these results so they can be returned directly without even touching the database. Especially when executing complex queries this can drop the load on your server significantly (in fact you could drop the load on your server by a whooping 90% by carefully inplementing caching in your web application).

Output caching is quite simple actually; all you have to do is to add an attribute to the methods that you want to cache, set the amount of seconds to store the result of that method and optionally set the location of the cache (client, server, combination, downstream, etc.). Below is an extremely simplified example to show you how it works:

public class FooController : Controller
{
    [OutputCache(Duration = 30, Location = OutputCacheLocation.ServerAndClient)]
    public ActionResult Foo()
    {
        // Fetch some data

        return View();
    }

    [OutputCache(Duration = 30)]
    public ActionResult Bar()
    {
        // Do some calculations, return some data

        return View();
    }
}

Sessionless controllers

MVC3 controllers come with session support by default. This means that controllers are able to store data values across requests. The process of creating and maintaining session state however consumes server memory and/or storage. Also it will waste extra CPU cycles that, probably, can be put to better use for most applications. As an added benefit it will be easier to run your web application across multiple server if you disable session state.

Again, disabling session state is quite easy, all you have to do is to mark your controller with the SessionState attribute as follows:

[SessionState(SessionStateBehavior.Disabled)]
public class FooController : Controller
{
    public ActionResult Foo()
    {
        // Fetch some data

        return View();
    }
}

Asynchronous controllers

If your application is I/O or network limited, it's a good option to consider implementing AsyncControllers, meaning that your controllers will be able to process multiple actions at the same time. One of the things you could do is to call a web service or fetch some data from the database. When you put this operation onto a separate work queue, the worker thread can immediately return to the worker pool and service other requests, making sure your application stays responsive. A good and easy way to implement asynchronous actions is by using the Task Parallel Library (TPL). In the example below I will give a brief explanation of this.

public class FooController : AsyncController
{
    public void FooAsync()
    {
        AsyncManager.OutstandingOperations.Increment(2);

        Task.Factory.StartNew(() => {
            // Perform some expensive operation

            AsyncManager.Parameters["data"] = data;
            AsyncManager.OutstandingOperations.Decrement();
        });
        Task.Factory.StartNew(() => {
            // Perform another expensive operation

            AsyncManager.Parameters["moredata"] = data;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    public ActionResult FooCompleted(string data, string moredata)
    {
        Foo foo = new Foo { Bar = data, Baz = moredata };

        return View(foo);
    }
}

As you can see I've splitted the action method into two separate methods (an async method that performs the operation(s) and a completed method that returns the data). It is important to name these methods as I did; start with the name of the action method, ending with 'Async' or 'Completed'.

In the Async method we start 2 new 'outstanding operations', and then we create 2 news tasks that do some processing, store the results of the operations by sending parameters to the AsyncManager and finally decreasing the amount of outstanding operations (marking the operation as finished). The parameters that we've sent to the AsyncManager will be automatically mapped to the parameters of the Completed method by looking at the names of the parameters. Finally the Completed method can return this data to the view that called the action method.

Client side validation

A very easy way to decrease the amount of hits on your web server is by implementing client side validation. Without client side validation, every time a user posts a form, the web server will receive a request, no matter if the request is valid or not. When using client side validation, the web server only receives a request when a form is filled in correctly, saving quite a few requests when working with enterprise applications that heavily rely on forms. Client side validation can't be any easier when working with MVC3, because all you have to do is to create a model that is annotated with some validation attributes and to include the JQuery validation library into your web application. The following example will demonstrate a basic example of this:

public class ChangePasswordModel
{
    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Current password")]
    public string OldPassword { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "New password")]
    public string NewPassword { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm new password")]
    [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

The model is annotated with Required attribute and a couple of other attributes that specify the rules that have to be met before the data can be submitted. Finally we just include the JQuery validation library in our _Layout.cshtml file:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title</title>
        <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
        <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    </head>
<body>

Deploying

When deploying your web application, make sure you disable debug mode in your Web.Config as follows:

<compilation debug="false" targetFramework="4.0">

Or you can override this setting in IIS Manager .NET Compilation Tool when using IIS 7.x or higher.

Conclusion

The steps above are the essential steps to get most out of your ASP.NET MVC3 web application. Output caching will definately deliver the biggest performance gain, with operation parallelization being another good candidate for improving the performance significantly. One thing you should always keep in mind is that you should always deploy your application in release mode, so it's best to write a deploy script to do this automatically for you, or by overriding this in your IIS Manager.

No comments

Powered by Blogger.