Customizing Output Caching in ASP.NET Web Forms and C# APIs

Efficient performance is essential for responsive, scalable web applications and one way to achieve that and boost performance is through caching. By avoiding repetitive data processing and rendering, caching allows applications to serve content more efficiently. In ASP.NET Web Forms and C# API-based applications, output caching and data caching can help improve load times and reduce server resource usage when applied appropriately.

Good Fit for Caching

  • Data is read often (rarely written)

  • Content is shared across users

  • Calls are slow or expensive

  • Data freshness is not business-critical

Not a Good Fit for Caching

  • Data is updated frequently or in real-time

  • Data is personalized or user-specific

  • Data is already fast or trivial to retrieve

  • Data freshness is business-critical

Some Benefits of Using Caching

  • Reduce server load. By serving cached responses, the server uses fewer CPU, disk, and memory resources.

  • Improve User Experience. Serving cached responses will make response time faster, smoother, and more responsive.

  • Scales Better Under Load. Serving cached responses will help the system handle high volumes of traffic without performing full processing for each incoming request.

Caching is a valuable performance tool, but it is not the only option. Other options such as asynchronous processing, batching, and queuing can also help improve responsiveness and reduce system load, especially when caching is not appropriate for situations where frequently updated data or tasks require the most current data.

Why Use Caching to Boot Performance?
Caching reduces the need to repeatedly perform expensive operations for things like database queries, page rendering, data serialization, and network and disk I/O.

  • Querying a database, especially with complex joins or large datasets can be time-consuming. Caching the result set means subsequent requests can bypass the database entirely and deliver the same data as a cached response.

  • Calling an external API or service (weather, stock prices, third-party data over HTTP).

  • Database access over the network (querying a SQL database hosted on another machine or cloud service).

  • Accessing a microservice (sending requests between services in a distributed system).

  • Retrieving remote files (documents, images, PDFs, etc.)

Computation or Business Logic
If a response depends on heavy processing (aggregating reports, business rules, etc.) caching can help avoid repeating that work for each request.

Rendering
Is typically for ASP.NET Web Form applications and is the process of generating HTML from server controls (or other content) that will be sent to the browser. In the context of ASP.NET Web Forms, this involves taking server-side controls (like GridView, Label, etc.) and converting them into HTML markup.

Serialization
Is typically for APIs and can be things like serializing return data into JSON or XML. Caching the final JSON or XML result (output caching) is pretty much skipping the rendering/serialization step for repeated (the same) inputs/outputs.

External Resource Access
Fetching data from third-party services or external APIs (weather, stock info, etc.) could introduce latency and maybe even rate limits. Caching these results reduces dependency on the third-party service and external API.

Network and Disk I/O
Reading static files or loading configuration from disk or across the network can also add delays. Caching them in memory will allow for quicker access to that information.

Output Caching
Output Caching in ASP.NET Web Forms
Output caching stores the final rendered HTML of a page and reuses it for subsequent requests. This is particularly effective for pages with static or content that rarely changes and can be represented as more static content.

To implement output caching in a Web Forms application, you can use the OutputCache directive:
<%@ OutputCache Duration="60" VaryByParam="none" %>

  • Duration defines how long (in seconds) the page is cached.

  • VaryByParam allows different versions of the cache based on query string or form parameters.

For example, to cache different versions of a product page by productId for 120 seconds
<%@ OutputCache Duration="120" VaryByParam="productId" %>

This ensures that each unique productId results in a distinct cached page.

You can also configure output caching programmatically in the page code-behind which provides more caching behavior control.

    Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(2));
    Response.Cache.SetCacheability(HttpCacheability.Public);
    Response.Cache.VaryByParams["productId"] = true;

Output Caching in C# APIs
ASP.NET Web API does not include built-in output caching like Web Forms does but basic HTTP response caching can be simulated by using a custom action filter. This filter sets caching headers on the response, allowing browsers or intermediary proxies to cache the result for a specified duration.

Here is a simple example defining a custom action filter:

public class OutputCacheAttribute : ActionFilterAttribute
{
  private readonly int _durationSeconds;

  public OutputCacheAttribute(int durationSeconds)
  {
      _durationSeconds = durationSeconds;
  }

  public override void OnActionExecuted(HttpActionExecutedContext context)
  {
    var response = context.Response;
    if (response != null)
    {
      response.Headers.CacheControl = new CacheControlHeaderValue
      {
        Public = true,
        MaxAge = TimeSpan.FromSeconds(_durationSeconds)
      };
    }
  }
}

You would then use the custom action filter in a controller action doing something like this. This caches the response for 120 seconds and reduces the load on the backend for repeated requests with the same parameters.

[OutputCache(120)]
public IHttpActionResult GetProduct(int id)
{
  var product = _productService.GetProductById(id);
  return Ok(product);
}

Data Caching
In addition to output caching, data caching offers greater flexibility and can be used effectively in both Web Forms and APIs. Data caching is the storing of data temporarily in memory so that it can be retrieved faster the next time it is needed. This reduces expensive operations such as repeated database calls.

Data Caching for ASP.NET Web Forms
In an ASP.NET Web Form application, data caching is implemented using the "System.Web.Caching.Cache" class. Example of getting a list of products and cache it for 5 minutes and then reuse it to avoid repeated "database" access.

ProductRepository.cs

public class ProductRepository
{
  public List<string> GetProducts()
  {
    return new List<string> { "Apple", "Banana", "Cherry" };
  }
}

Default.aspx.cs

public partial class _Default : System.Web.UI.Page
{
  protected void Page_Load(object sender, EventArgs e)
  {
    if (!IsPostBack)
    {
      var products = GetProductsFromCache();
      ListBox1.DataSource = products;
      ListBox1.DataBind();
    }
  }

  private List<string> GetProductsFromCache()
  {
    var products = Cache["ProductList"] as List<string>;
    if (products == null)
    {
      var repo = new ProductRepository();
      products = repo.GetProducts();
      Cache.Insert("ProductList", products, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration);
    }
    return products;
  }
}

Default.aspx
<asp:ListBox ID="ListBox1" runat="server" Width="200"></asp:ListBox>

Data Caching in an ASP.NET Core API
In ASP.NET Core APIs, data caching is commonly implemented using the built-in IMemoryCache service. This allows you to store frequently requested data (such as database query results) in memory to reduce redundant database calls and improve performance. Example of getting a list of products and cache it for 5 minutes and then reuse it to avoid repeated "database" access.

ProductRepository.cs

public class ProductRepository
{
  public List<string> GetProducts()
  {
    return new List<string> { "Apple", "Banana", "Cherry" };
  }
}

ProductController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;

[ApiController]
[Route("products")]
public class ProductsController : ControllerBase
{
  private readonly IMemoryCache _cache;
  private readonly ProductRepository _repo = new ProductRepository();

  public ProductsController(IMemoryCache cache)
  {
    _cache = cache;
  }

  [HttpGet]
  public List<string> Get()
  {
    if (!_cache.TryGetValue("ProductList", out List<string> products))
    {
      products = _repo.GetProducts();
      _cache.Set("ProductList", products, TimeSpan.FromMinutes(5));
    }

    return products;
  }
}

Program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMemoryCache();
builder.Services.AddControllers();

var app = builder.Build();
app.MapControllers();
app.Run();

Conclusion
Caching is a good way to boost ASP.NET Web Forms and C# API application performance.

Output caching is a good way to speed up an entire page or response and is ideal for quickly serving complete pages or responses.
Data Caching is a good way to improve business logic or data access performance and supports reusability and responsiveness at the business logic layer.

Also, it is handy that you can control how long things stay in the cache and even change what gets cached based on things like query strings or user roles. Basically, you can cache what makes sense when it makes sense and also refresh the cache when required.

0
Subscribe to my newsletter

Read articles from Sean M. Drew Sr. directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sean M. Drew Sr.
Sean M. Drew Sr.