Introduction: Why Caching is a Critical Component of C# Applications
\n\nIn modern web development, application performance directly impacts user experience and business metrics. Every second of page load delay can cost you 7% of conversions. Caching in C# (.NET) is not just a "nice option" but a mandatory tool for any serious developer.
\n\nIn this article, we will cover three main approaches to caching in the .NET ecosystem: Redis, Memcached, and built-in solutions (MemoryCache). You will learn when to use which tool, how to properly configure the cache, and how to avoid common mistakes. The material will be useful for both beginner C# developers and experienced engineers looking to systematize their knowledge.
\n\n1. Built-in Caching: MemoryCache and IDistributedCache
\n\nThe .NET platform provides developers with several built-in caching mechanisms that do not require installing third-party services.
\n\n1.1 MemoryCache — Local Cache for a Single Server
\n\nIMemoryCache is the simplest and fastest way to cache data in C#. Data is stored in the RAM of the current process. It is ideal for single-threaded applications or applications running on a single server.
using Microsoft.Extensions.Caching.Memory;\n\npublic class ProductService\n{\n private readonly IMemoryCache _cache;\n \n public ProductService(IMemoryCache cache)\n {\n _cache = cache;\n }\n\n public async Task<Product> GetProductAsync(int id)\n {\n string cacheKey = $"product_{id}";\n \n if (!_cache.TryGetValue(cacheKey, out Product product))\n {\n // Expensive operation (e.g., database query)\n product = await GetProductFromDatabaseAsync(id);\n \n var cacheOptions = new MemoryCacheEntryOptions()\n .SetAbsoluteExpiration(TimeSpan.FromMinutes(30))\n .SetSlidingExpiration(TimeSpan.FromMinutes(5))\n .SetSize(1);\n \n _cache.Set(cacheKey, product, cacheOptions);\n }\n \n return product;\n }\n}\n\nKey Features of MemoryCache:
\n- \n
- High speed — data is read directly from RAM \n
- No serialization — stores objects as-is \n
- Limitation — does not work in a server farm (each server has its own cache) \n
- Risk — the cache is completely cleared when the application restarts \n
1.2 IDistributedCache — Abstraction for Distributed Cache
\n\nThe IDistributedCache interface provides a unified API for working with any distributed cache. You can switch between Redis, SQL Server, or NCache without changing business logic code.
using Microsoft.Extensions.Caching.Distributed;\nusing System.Text.Json;\n\npublic class WeatherService\n{\n private readonly IDistributedCache _cache;\n \n public WeatherService(IDistributedCache cache)\n {\n _cache = cache;\n }\n\n public async Task<WeatherForecast> GetForecastAsync(string city)\n {\n string cacheKey = $"weather_{city}";\n string cachedData = await _cache.GetStringAsync(cacheKey);\n \n if (cachedData != null)\n {\n return JsonSerializer.Deserialize<WeatherForecast>(cachedData);\n }\n \n var forecast = await FetchFromApiAsync(city);\n var serialized = JsonSerializer.Serialize(forecast);\n \n await _cache.SetStringAsync(cacheKey, serialized, new DistributedCacheEntryOptions\n {\n AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15)\n });\n \n return forecast;\n }\n}\n\n