Why Caching is a Superpower for Every C# Developer
\nImagine: your application goes to the database every time for the same list of cities or exchange rates. A database is a slow and expensive resource. What if we told you that you could respond to the user in 1 millisecond instead of 100? Yes, this is not magic, but smart data caching in C#.
\nCaching is a technique of temporarily storing frequently requested data in fast storage (usually in RAM). In this article, we will look at how to implement caching in .NET applications using both built-in mechanisms and external solutions. You will learn how to choose the right strategy and avoid the trap of stale data.
\n\nTypes of Caching in C#: From MemoryCache to Redis
\nIn the .NET ecosystem, there are several approaches to caching. The choice depends on your application's architecture: monolith or microservices, single server or cluster.
\n\n1. Built-in MemoryCache (System.Runtime.Caching / Microsoft.Extensions.Caching.Memory)
\nThe simplest and fastest way is to use IMemoryCache. Data is stored in the process's RAM. Ideal for single-threaded applications or simple websites.
Example of registration and usage in ASP.NET Core:
\n// In Program.cs\nbuilder.Services.AddMemoryCache();\n\n// In a controller or service\npublic class WeatherService\n{\n private readonly IMemoryCache _cache;\n\n public WeatherService(IMemoryCache cache)\n {\n _cache = cache;\n }\n\n public async Task<string> GetWeatherAsync(string city)\n {\n string cacheKey = $"weather_{city}";\n\n // Try to get data from cache\n if (_cache.TryGetValue(cacheKey, out string cachedData))\n {\n return cachedData;\n }\n\n // If data is not found — load it (e.g., from API)\n string weatherData = await FetchWeatherFromApi(city);\n\n // Save to cache for 10 minutes\n var cacheEntryOptions = new MemoryCacheEntryOptions()\n .SetAbsoluteExpiration(TimeSpan.FromMinutes(10));\n\n _cache.Set(cacheKey, weatherData, cacheEntryOptions);\n\n return weatherData;\n }\n}\nKey methods: TryGetValue, Set, Remove. Pay attention to expiration policies: absolute expiration (data dies exactly after N minutes) and sliding expiration (data lives as long as it is accessed, and for N minutes after the last request).
2. Distributed Cache: Redis via IDistributedCache
\nIf you have multiple application instances (web farm, microservices), MemoryCache is not suitable — each server will store its own copy. Redis comes to the rescue. It works as a separate server, shared across all instances.
\nConnecting Redis in C#:
\n// Install the package: Microsoft.Extensions.Caching.StackExchangeRedis\nbuilder.Services.AddStackExchangeRedisCache(options =>\n{\n options.Configuration = "localhost:6379";\n options.InstanceName = "SampleInstance";\n});\n\n// Usage via IDistributedCache\npublic class ProductService\n{\n private readonly IDistributedCache _cache;\n\n public ProductService(IDistributedCache cache)\n {\n _cache = cache;\n }\n\n public async Task<Product> GetProductAsync(int id)\n {\n string cacheKey = $"product_{id}";\n var cachedProduct = await _cache.GetStringAsync(cacheKey);\n\n if (cachedProduct != null)\n {\n return JsonSerializer.Deserialize<Product>(cachedProduct);\n }\n\n var product = await FetchProductFromDb(id);\n var serialized = JsonSerializer.Serialize(product);\n\n await _cache.SetStringAsync(cacheKey, serialized, new DistributedCacheEntryOptions\n {\n AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)\n });\n\n