Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A distributed cache can improve an ASP.NET Core application when multiple requests repeatedly perform expensive work—such as database queries or remote-service calls—and those requests may be handled by different application servers. The cache shares entries across nodes, but it also adds network I/O, serialization, expiration and invalidation work. The reliable approach is to profile first, cache only suitable data, integrate through IDistributedCache, and verify the result with workload-specific measurements.

When a distributed cache helps ASP.NET Core

Start with the request paths that consume the most time or resources. Microsoft’s ASP.NET Core guidance describes these as hot code paths. Database and remote-service calls are common candidates because repeating them for identical inputs can waste both latency and backend capacity.

Cache a value when it is requested frequently, expensive to reproduce, and allowed to be slightly stale. A cache is usually a poor fit for data that is rarely requested, cheap to calculate, or required to be immediately consistent after every write.

  • Profile the endpoint and its data-access calls before adding caching.
  • Identify repeated reads and the inputs that change their result.
  • Confirm that the product can tolerate the planned period of staleness.
  • Estimate the cost of a miss, including the source query and serialization.

Choose local memory or a shared cache

In-process memory avoids a network hop and can be effective on one server. It can also work when session affinity reliably sends a client to the same server. In a scale-out deployment, however, each node has a different local cache. A request routed to another node cannot use the first node’s entries.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A distributed cache is external to the application process and shared by the servers. Microsoft notes that it can keep cached data coherent across requests handled by multiple servers and can preserve entries through application-server restarts and deployments. The trade-off is network access and the operational cost of another service; distributed caching introduces some latency even when the cache itself is fast.

Option Shared across nodes Appropriate use Important trade-off
In-process memory cache No Single-server applications or deployments with dependable session affinity Entries are isolated per process and are lost when that process restarts
Redis Yes Production scale-out workloads needing a dedicated, high-performance cache Requires a Redis service, network access, operational cost and workload validation
SQL Server Yes Teams that already operate SQL Server and need a supported distributed provider Microsoft recommends a dedicated SQL Server instance; sharing the application database can reduce performance
PostgreSQL, NCache or Azure Cosmos DB Yes Environments where the provider fits existing infrastructure and requirements Latency, cost, persistence and operational behavior must be measured for the workload
Distributed memory cache No Development and testing Stores entries in the application process and is not a shared production deployment

Microsoft’s current distributed-cache guidance recommends Redis for production in general and describes it as the best-performing option among the documented choices, while also advising teams to consider infrastructure, performance requirements, cost and experience. That is guidance, not a guaranteed result for every topology; benchmark the candidates you can operate.

Integrate through IDistributedCache

Register one provider with dependency injection and depend on the framework abstraction in application code. IDistributedCache addresses entries with string keys and stores values as byte arrays. It exposes synchronous and asynchronous get, set, refresh and remove operations.

Register Redis

Install the Microsoft.Extensions.Caching.StackExchangeRedis package and register the provider during startup:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "MyApp:";
});

Keep the connection string and credentials out of source control. Microsoft’s examples use Secret Manager for local development and a secure store such as Azure Key Vault for Azure-hosted applications.

Use the abstraction in a service

A cache-aside service checks the cache first, loads the source on a miss, stores the serialized value, and returns it. Use asynchronous methods on request paths so a blocked cache or database call does not tie up a Thread Pool thread.

public sealed class ProductReader(IDistributedCache cache, ProductDb db)
{
    public async Task<Product?> GetAsync(string tenant, int id, CancellationToken cancellationToken)
    {
        var key = $"products:{tenant}:{id}";
        var bytes = await cache.GetAsync(key, cancellationToken);

        if (bytes is not null)
            return JsonSerializer.Deserialize<Product>(bytes);

        var product = await db.FindAsync(tenant, id, cancellationToken);
        if (product is null)
            return null;

        var payload = JsonSerializer.SerializeToUtf8Bytes(product);
        var options = new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
            SlidingExpiration = TimeSpan.FromMinutes(2)
        };

        await cache.SetAsync(key, payload, options, cancellationToken);
        return product;
    }
}

The serialization format is your responsibility. Account for payload size, serialization CPU time and compatibility when models change. A value that is expensive to serialize or too large to transfer can erase the benefit of caching.

Design keys, expiration and invalidation together

Make keys encode every input

Include every value that can change the result, such as tenant, locale, entity identifier and relevant query parameters. Prefix keys by feature and environment to prevent collisions—for example, production:catalog:tenant-42:en-US:item-17. Never let a value for one tenant or locale be returned for another.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Set an evidence-based lifetime

DistributedCacheEntryOptions supports absolute and sliding expiration. Absolute expiration limits the maximum age. Sliding expiration extends an entry when it is accessed; Refresh or RefreshAsync can reset that sliding window. Choose durations from the source system’s update frequency and the user’s tolerance for stale data, not from a universal “best” TTL.

Handle writes explicitly

Expiration alone does not make a cache correct after a source-of-truth write. On updates, decide whether to remove the affected key, write the new value, or use versioned keys. For related entries, define which keys are invalidated together. If the endpoint cannot tolerate stale data, bypass the cache or use a consistency strategy that your provider and application can enforce.

Prevent misses and cache failures from becoming bottlenecks

Keep the miss path deliberate

On a miss, retrieve the required source data with as few round trips as practical, then populate the cache. Concurrent misses for the same key can create a stampede against the database; where this is a real risk, add a per-key coordination or request-coalescing design appropriate to your application.

Plan expiration bursts and large entries

Stagger lifetimes where a large group of entries is loaded together, and review payload size limits before storing objects. Monitor backend load around deployments and known expiration boundaries.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose a failure policy

A cache outage is an application design case, not an automatic framework behavior. For each endpoint, decide whether to fall back to the source database, return an error, or serve a bounded stale value. The choice depends on correctness, source capacity and availability objectives. Put timeouts and cancellation on cache calls so an unhealthy cache cannot hold requests indefinitely.

Do not block asynchronous work

Prefer GetAsync, SetAsync, RefreshAsync and RemoveAsync in asynchronous request flows. Blocking on asynchronous operations can contribute to Thread Pool starvation and worse response times.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Compare providers using your workload

Evaluate candidate providers on the following axes:

  • Whether entries must be shared across application nodes.
  • Measured hit latency, miss latency and throughput under representative load.
  • Fit with existing infrastructure and operational skills.
  • Total service, licensing and engineering cost.
  • Behavior during restarts, deployments and connectivity failures.
  • Expiration, persistence and invalidation capabilities needed by the data.
  • Team familiarity with administration, security and diagnostics.

Redis is Microsoft’s general production recommendation for distributed caching, but a SQL Server, PostgreSQL, NCache or Azure Cosmos DB provider may be a better operational fit in a particular environment. If SQL Server is selected, use a dedicated cache instance rather than competing with ordinary application data on the same database.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use output caching for HTTP responses

IDistributedCache is intended for application data entries. It is not the recommended store for ASP.NET Core output caching because the interface lacks the atomic features required for output-cache tagging.

For server-controlled HTTP response caching, configure ASP.NET Core output-caching middleware and its IOutputCacheStore integration. Redis output caching uses the dedicated Microsoft.AspNetCore.OutputCaching.StackExchangeRedis package and AddStackExchangeRedisOutputCache. Do not substitute an ordinary distributed-data cache merely because both features store bytes.

Measure before and after

Record a baseline before enabling the cache, then repeat the same representative workload after the change. At minimum, capture:

  • Request latency at useful percentiles, not only the average.
  • Throughput and error rate.
  • Cache hit, miss, set, refresh and removal counts.
  • Cache-operation latency, including timeouts and failures.
  • Source-database or remote-service query volume and latency.
  • CPU, memory, network and connection usage for both the application and cache.

Test warm-cache, cold-cache, expiration, concurrent-miss and cache-outage scenarios. Compare the extra network and serialization work with the source work removed. Keep the cache only if the measured improvement in the target endpoint or backend capacity justifies its operational and correctness complexity; there is no universal latency or throughput gain to assume without a benchmark for your application.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.