ASP.NET
MVC
application performance
web development
optimization

How do I improve ASP.NET MVC application performance?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Most ASP.NET MVC performance problems are not caused by MVC itself. They usually come from slow database access, unnecessary rendering work, large payloads, or blocking calls to other services. The fastest route to improvement is to measure first and then optimize the slowest layer instead of guessing.

Measure Before You Change Anything

The first step is visibility. At minimum, track request duration, database query counts, and calls to external services.

A simple action filter can help surface slow endpoints during development:

csharp
1using System.Diagnostics;
2using System.Web.Mvc;
3
4public class RequestTimingFilter : ActionFilterAttribute
5{
6    private const string Key = "__request_timer";
7
8    public override void OnActionExecuting(ActionExecutingContext filterContext)
9    {
10        filterContext.HttpContext.Items[Key] = Stopwatch.StartNew();
11    }
12
13    public override void OnActionExecuted(ActionExecutedContext filterContext)
14    {
15        if (filterContext.HttpContext.Items[Key] is Stopwatch sw)
16        {
17            sw.Stop();
18            Debug.WriteLine(
19                $"{filterContext.ActionDescriptor.ControllerDescriptor.ControllerName}/" +
20                $"{filterContext.ActionDescriptor.ActionName}: {sw.ElapsedMilliseconds} ms"
21            );
22        }
23    }
24}

Without measurement, teams often optimize code that is not actually on the hot path.

Reduce Database Cost First

Database access is often the biggest source of latency. The high-value improvements are usually:

  • querying only the columns you need
  • avoiding N+1 query patterns
  • using AsNoTracking() for read-only work
  • adding indexes for common filters and sorts

Example with Entity Framework:

csharp
1public async Task<ActionResult> List()
2{
3    var products = await _db.Products
4        .AsNoTracking()
5        .Where(p => p.IsActive)
6        .OrderBy(p => p.Name)
7        .Select(p => new ProductRowVm
8        {
9            Id = p.Id,
10            Name = p.Name,
11            Price = p.Price
12        })
13        .Take(100)
14        .ToListAsync();
15
16    return View(products);
17}

That reduces tracking overhead, network payload, and view complexity at the same time.

Cache Expensive Stable Results

Caching works best when the result is expensive to compute and does not change constantly.

For output caching:

csharp
1[OutputCache(Duration = 60, VaryByParam = "none")]
2public ActionResult Catalog()
3{
4    var vm = _catalogService.BuildCatalogVm();
5    return View(vm);
6}

For in-memory data caching:

csharp
1using System;
2using System.Runtime.Caching;
3
4public class ExchangeRateService
5{
6    private readonly ObjectCache _cache = MemoryCache.Default;
7
8    public decimal GetRate(string currency)
9    {
10        string key = "rate:" + currency;
11        if (_cache[key] is decimal cached)
12            return cached;
13
14        decimal rate = FetchRateFromApi(currency);
15        _cache.Set(key, rate, DateTimeOffset.UtcNow.AddMinutes(5));
16        return rate;
17    }
18
19    private decimal FetchRateFromApi(string currency) => 1.0m;
20}

The hard part is not adding a cache. It is knowing when the data becomes stale and how to invalidate it safely.

Use Async for I/O-Bound Work

Async controller actions improve throughput when requests spend time waiting on the database or remote services:

csharp
1public async Task<ActionResult> Details(int id)
2{
3    var product = await _db.Products
4        .AsNoTracking()
5        .FirstOrDefaultAsync(p => p.Id == id);
6
7    if (product == null)
8        return HttpNotFound();
9
10    return View(product);
11}

Async does not speed up CPU-heavy code, but it does free request threads while the application is waiting on I/O.

Trim Rendering and Asset Work

Controllers are not the only performance factor. Large Razor views, repeated partial rendering, and oversized client assets all add up.

Bundle and minify front-end assets:

csharp
1using System.Web.Optimization;
2
3public static void RegisterBundles(BundleCollection bundles)
4{
5    bundles.Add(new ScriptBundle("~/bundles/app")
6        .Include("~/Scripts/jquery-{version}.js")
7        .Include("~/Scripts/app/*.js"));
8
9    bundles.Add(new StyleBundle("~/Content/css")
10        .Include("~/Content/site.css")
11        .Include("~/Content/theme.css"));
12}

Also keep view models focused. If a page only needs six fields, do not pass an entire heavy entity graph into the view.

Look Beyond MVC Code

An MVC app that feels slow may actually be waiting on:

  • SQL queries
  • Redis or cache lookups
  • remote APIs
  • file storage
  • overloaded thread pools

That is why load testing and dependency monitoring matter. Local single-user testing can hide problems that only appear under concurrency.

Common Pitfalls

The biggest mistake is optimizing without a baseline. If you do not know which endpoint or dependency is slow, your fixes may be irrelevant.

Another issue is caching without a clear invalidation rule. A fast stale answer is still a bug.

Teams also adopt async controller methods while keeping synchronous database or HTTP calls underneath, which adds complexity without removing the blocking cost.

Finally, people often blame MVC for performance problems that are really caused by inefficient queries or dependency latency elsewhere in the stack.

Summary

  • Measure request timing, query cost, and dependency latency before optimizing.
  • Reduce database overhead with projection, indexing, and read-only query patterns.
  • Cache only work that is expensive and stable enough to reuse safely.
  • Use async for I/O-bound request paths, not as a general performance ritual.
  • Treat performance as an end-to-end systems problem, not just a controller problem.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.