Thursday, 8 January 2015

Improving ASP.NET MVC Performance with Output Caching

You can dramatically improve the performance of your ASP.NET MVC application by taking advantage of the framework's built-in output caching mechanism. If you have a high-traffic page or an action that queries a large dataset from the database, output caching allows you to cache the HTML or JSON response directly in memory. By serving cached content for subsequent requests, you eliminate unnecessary database hits and drastically lower page load times.

In ASP.NET MVC 4 and 5, output caching can be implemented quickly using the standard [OutputCache] attribute without needing to build custom cache providers. Decorating your controller actions with this attribute gives you granular control over cache duration, location, and invalidation rules.

Default Caching Behavior in ASP.NET MVC

By default, GET actions in ASP.NET MVC return a Cache-Control header set to private. Consider a standard AJAX endpoint returning JSON:


public JsonResult Details(int id)
{
    // Fetch data for the specified ID
    var result = _repository.GetDetails(id);

    return Json(result, JsonRequestBehavior.AllowGet);
}

If you inspect the response headers using Fiddler or Browser Developer Tools, you will see output similar to this:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-AspNetMvc-Version: 4.0

Because Cache-Control is set to private, browsers (especially Internet Explorer) might cache these AJAX responses locally for the current user session. This can lead to stale data or unexpected application behavior when underlying data changes. Explicitly configuring output caching gives you predictability and control over client and server cache rules.

Enabling the [OutputCache] Attribute

To control caching explicitly, decorate your action method with the [OutputCache] attribute and specify a Duration (in seconds):


[OutputCache(Duration = 6)]


public ActionResult Search(string searchText)
{


return Json(SearchWebService.MyWebServiceInstance.AutocompleteSearchClient(searchText),JsonRequestBehavior.AllowGet);

}

Inspecting the HTTP response headers now reveals updated cache directives:

HTTP/1.1 200 OK
Cache-Control: public, no-transform, max-age=60
Expires: Mon, 29 Oct 2015 09:18:37 GMT
Last-Modified: Mon, 29 Oct 2015 09:17:37 GMT
Content-Type: application/json; charset=utf-8

Testing It in Visual Studio

  1. Remove the [OutputCache] attribute and place a breakpoint inside your action method.

  2. Launch your application and press F5 (Refresh) in your browser—the breakpoint hits on every request.

  3. Add [OutputCache(Duration = 60)] back to the action and refresh again.

  4. The breakpoint hits once for the initial request. Subsequent refreshes within the 60-second window serve cached content instantly without stepping into the controller action.

Handling Dynamic Content with VaryByParam

When an action returns different content based on query strings or route parameters, you must instruct the cache engine to store separate instances for each variation using VaryByParam.

  • VaryByParam = "none": Ignores all incoming parameters and serves the exact same cached payload to every user.

  • VaryByParam = "*": Creates a unique cache entry for every unique combination of query parameters.

  • Explicit Parameter List: Specify exact parameters separated by semicolons (e.g., VaryByParam = "accountId;page").

For example, when caching a paginated or sorted grid:


[OutputCache(Duration = 1, VaryByParam = "*")]
public ActionResult Index(int accountId, string page, string sort, string sortdir)
{

///code to get all accounts



return PartialView("_AccountsList", data);

}

Using VaryByParam = "*" guarantees that requests for ?accountId=1&page=2 and ?accountId=1&page=3 receive their corresponding cached views rather than returning the same cached data for both pages.

File upload in MVC3/4

Those new to MVC or coming from classic ASP.NET might be a little daunted when first tasked with uploading a file in MVC , as there's no concept of the fileupload control (or any server side control for that matter)?

Thanks to Darin Dimitrov for the great example here:

Your controller

public class HomeController : Controller
{
// This action renders the form
public ActionResult Index()
{
return View();
}
// This action handles the form POST and the upload
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
// Verify that the user selected a file
if (file != null && file.ContentLength > 0)
{
// extract only the fielname
var fileName = Path.GetFileName(file.FileName);
// store the file inside ~/App_Data/uploads folder
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
// redirect back to the index action to show the form once again
return RedirectToAction("Index");
}
}


The view


@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="file" />

<input type="submit" value="OK" />
}