Skip to main content

Outline

At a glance:
  • What it is: Caching reusable fragments like blocks and product cards.
  • Why it matters: Reduces rendering time and server load.
  • Core APIs: IMemoryCache, IDistributedCache, OutputCache.
  • Optimizely advantage: IContentCacheKeyService enables smart invalidation.
  • DXP rule: Use distributed caching in multi-node environments.

This module provides a technical deep dive into partial (fragment) caching within ASP.NET Core-based Optimizely solutions, covering in-memory and distributed caching patterns with practical implementation examples.

1. In-Memory Caching (IMemoryCache)

IMemoryCache stores cached data in the memory of the running process. It is suitable for single-node deployments but should not be used in multi-node DXP environments where cache entries would be inconsistent across instances.

Registration (Program.cs)

C#
// Program.cs builder.Services.AddMemoryCache();

Usage in a Service

C#
public class MyService { private readonly IMemoryCache _memoryCache; public MyService(IMemoryCache memoryCache) { _memoryCache = memoryCache; } public string GetCachedData(string key) { if (!_memoryCache.TryGetValue(key, out string cachedValue)) { cachedValue = FetchDataFromDatabase(); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(5)) .SetAbsoluteExpiration(TimeSpan.FromMinutes(30)); _memoryCache.Set(key, cachedValue, cacheEntryOptions); } return cachedValue; } }

Important: IMemoryCache is process-local. In a multi-node DXP environment, each server maintains its own independent cache, meaning updates on one node will not invalidate cached entries on others. Use IDistributedCache instead.

2. Distributed Caching (IDistributedCache)

IDistributedCache stores cached data in a shared external store such as Azure Cache for Redis, making it consistent across all nodes in a multi-server deployment. This is the correct approach for DXP environments.

Registration with Redis (Program.cs)

C#
// Program.cs builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration.GetConnectionString("RedisCache"); options.InstanceName = "OptimizelyApp_"; });

Async Usage in a Service

C#
public class MyService { private readonly IDistributedCache _distributedCache; public MyService(IDistributedCache distributedCache) { _distributedCache = distributedCache; } public async Task<string> GetCachedDataAsync(string key) { byte[] encodedValue = await _distributedCache.GetAsync(key); string cachedValue; if (encodedValue != null) { cachedValue = Encoding.UTF8.GetString(encodedValue); } else { cachedValue = await FetchDataFromDatabaseAsync(); byte[] valueToCache = Encoding.UTF8.GetBytes(cachedValue); var options = new DistributedCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(5)) .SetAbsoluteExpiration(TimeSpan.FromMinutes(30)); await _distributedCache.SetAsync(key, valueToCache, options); } return cachedValue; } }

Pro tip: The IDistributedCache API works with raw byte arrays. Pair it with a serialization helper (JSON, MessagePack, or Protobuf) to avoid scattering Encoding.UTF8 calls throughout your services. A thin generic wrapper such as ICacheService<T> keeps things consistent and testable.