Back to: ASP.NET Core Web API Tutorials
How to Implement Redis Cache in ASP.NET Core Web API
In this article, I will discuss how to Implement a Redis Cache in an ASP.NET Core Web API Application with Examples. Please read our previous articles on Customizing In-Memory Cache in ASP.NET Core Web API Applications with Examples.
What is Distributed Caching?
Distributed Caching is a caching mechanism where data is stored in an external cache system that can be accessed by multiple application servers simultaneously. That means Distributed caching (such as Redis, NCache, etc.) allows multiple application instances to share cached data. Unlike an in-memory cache that is local to one application instance, a distributed cache is typically hosted on a dedicated cache server, i.e., a distributed cache stores data in an external system. All application servers connect to this cache server (external system) to get/set cached data. This design improves application scalability and reliability, as cached data remains accessible even if an application server restarts.
When Should We Use a Distributed Cache Instead of In-Memory Cache?
In-memory caching is fast but is limited to a single application instance. If the application is deployed across multiple servers or containers, each instance will have its own separate in-memory cache. This can lead to issues such as cache duplication, increased memory usage, and inconsistent data. In these cases, distributed caching is preferred because it provides a centralized cache that all application instances can access. Additionally, distributed caching is more reliable if an application instance goes down, as the cached data remains available in the external cache-store. So, we need to use a Distributed Cache Instead of In-Memory Cache in the following scenarios:
- Multiple Server Instances: If your application runs on multiple servers behind a load balancer, you need a cache that all servers can share. An in-memory cache would be unique to each server, causing inconsistent data or missing data if the request is routed to a different server.
- Memory Constraints: Maintaining large caches in each server’s memory can be expensive. Offloading the cache to an external system can be more cost-effective and memory-efficient on the app servers.
- Persistence Across Restarts: In-memory caches are lost whenever the application restarts. Distributed cache solutions can offer persistence and advanced features like replication, meaning your data remains available even if an app server goes down.
- High Scalability: In scenarios with multiple application servers, using an in-memory cache can lead to data inconsistency or duplication because each server holds its own cache. A distributed cache maintains a single source of cached data accessible to all servers.
- High Availability: With load balancing, a distributed cache ensures that a cache hit is possible regardless of which server handles the request.
Distributed Caching Architecture:
Let us first understand the architecture of Distributed Caching, and then we will see how to implement Distributed caching using Redis cache in our ASP.NET Core Web API Application. In a distributed caching architecture, several components work together to manage, retrieve, and store cached data efficiently. These components include users, a load balancer, application servers, the distributed cache, and the database. To understand the architecture of Distributed Caching, please have a look at the following diagram.
Components of the Distributed Caching:
- Users: The clients or consumers that send requests to the application. Users can be end-users accessing a website or API clients consuming the application’s endpoints.
- Load Balancer: The load balancer distributes incoming requests across multiple application server instances. This ensures that no single server is overloaded, maintains high availability, and evenly distributes workloads.
- App Servers: These are the application server instances that process requests. They retrieve or store data in the distributed cache, reducing the need to query the database repeatedly. Multiple app servers allow the system to scale horizontally, handling more concurrent requests as traffic grows.
- Distributed Cache: The centralized storage for cached data that is accessible by all application servers. It improves performance by providing quick access to frequently requested data. Unlike local in-memory caches, distributed caches ensure consistency and reduce redundancy.
- Database: The primary source of persistent data. When a requested piece of data is not available in the cache (a cache miss), the application server queries the database, fetches the data, and often stores it in the cache for future requests.
How Distributed Caching Works:
- User Request: Users send a request to the application, which first passes through the load balancer.
- Load Balancer: The load balancer routes the request to one of the application servers.
- Cache Check: The application server checks if the data is available in the distributed cache.
- Cache Hit: If the requested data is found (cache hit) in the distributed cache, the data is retrieved quickly from the cache.
- Cache Miss: If the data is not found in the cache, it is called a Cache Miss. In this case, the App Server queries the Database to retrieve the data.
- Store Data in Cache: After retrieving the data from the database, the app server stores it in the distributed cache. Subsequent requests for the same data can be served from the cache instead of the database.
- Return Data: The data, whether retrieved from the cache or database, is returned to the user.
What is Redis?
Redis (short for Remote Dictionary Server) is an open-source, in-memory data store primarily used as a caching solution, database, and message broker. It stores data in key-value pairs and supports advanced data structures (e.g., lists, sets, sorted sets, hashes). Redis is Known for:
- High Performance: Data is stored in memory, making read and write operations extremely fast compared to disk-based storage.
- Rich Data Structures: It supports a variety of data structures, such as strings, hashes, lists, sets, and sorted sets, making it suitable for a wide range of use cases.
In ASP.NET Core Web API applications, Redis is commonly used as a distributed caching solution. It helps store frequently accessed data in memory so that subsequent requests can be served quickly without hitting the slower underlying database.
Setting up Redis on Windows 10/11
While Redis officially supports Linux and macOS, there are a few workarounds for running Redis on Windows. Let us proceed and see how we can set up Redis on a Windows 10/11 machine:
Download the Windows-compatible version of Redis:
Please visit the following GitHub URL to download a Windows-compatible version of Redis supported by Microsoft:
https://github.com/microsoftarchive/redis/releases/tag/win-3.0.504
Once you visit the above URL, it will open the page below. From this page, download the Redis-x64-3.0.504.zip file from the GitHub Repo on your machine, as shown below.
Extract the ZIP File:
Once you click on the above Redis-x64-3.0.504.zip file, it will download the ZIP folder. Then, extract the ZIP folder to a preferred location on your machine, and you should see the following. This folder will contain the Redis server executable (redis-server.exe) and the command line interface (redis-cli.exe).
Start the Redis Server:
Navigate to the extracted folder and double‑click on redis-server.exe to start the Redis server. A console window should open to show that Redis is running. By default, Redis listens on port 6379. Keep this window open to ensure the Redis server continues running. Please observe the port number (6379) on which the Redis server is running, which we will use in our ASP.NET Core Application to set and get the cache data.
Test the Redis Server:
Let’s check if the Redis server is up and accessible. To do so, open redis-cli.exe (located in the same folder). In the CLI window, type the command PING and press Enter. If Redis is running correctly, you will receive a PONG response, as shown in the image below. This is a simple test to see if the server is alive.
Basic Redis Commands:
The following are some of the Basic Commands:
- Set a Value: SET Country “India”
- Get a Value: GET Country
- Delete a Key: DEL Country
Now, we can use the SET command to set a new key-value-based cache item. In the below example, I used the key Country and the value India. To retrieve the cache value, we can use the GET command with the cache key. Similarly, to delete the cache key, you need to use the DEL command.
Integrating Redis Cache in ASP.NET Core Web API:
Imagine we have an e-commerce website where users frequently access product information. Instead of querying the database for each request for product details, the data can be cached in the distributed cache. When a user requests product details, the application server can directly fetch the data from the cache, thus providing a faster response.
Let’s see how to implement Redis Cache in our ASP.NET Core Web API Application. We will create a simple ASP.NET Core Web API project that uses Entity Framework Core to query and return Products from the SQL Server database.
Project setup:
So, first, create a new ASP.NET Core Web API project named RedisCachingDemo. Once you create the project, we need to add the following Packages for Entity Framework Core to work with the SQL Server database and Redis cache. You can install these packages using the following commands in the Package Manager Console.
- Install-Package Microsoft.EntityFrameworkCore.SqlServer
- Install-Package Microsoft.EntityFrameworkCore.Tools
- Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
Note: To connect and start caching data from .NET Core applications with Redis cache, we need Microsoft.Extensions.Caching.StackExchangeRedis package.
Define the Database Model and DbContext
In our example, we need to fetch the Products master data. First, create a new folder named Models in the project root directory. Then, inside the Models folder, create a class file named Product.cs and copy and paste the following code.
namespace RedisCachingDemo.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public int Price { get; set; } public int Quantity { get; set; } } }
Create Db Context:
Next, we need to create the DbContent class. First, create a new folder named Data in the project root directory. Then, inside the Data folder, create a class file named ApplicationDbContext.cs and copy and paste the following code. Here, we are also inserting some initial data for testing purposes.
using Microsoft.EntityFrameworkCore; using RedisCachingDemo.Models; namespace RedisCachingDemo.Data { public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } // Override this method to configure the model and seed initial data protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Seed initial data for testing purposes. // Each Product instance must have a unique Id. var initialProducts = new List<Product> { new Product { Id = 1, Name = "Apple iPhone 14", Category = "Electronics", Price = 999, Quantity = 50 }, new Product { Id = 2, Name = "Samsung Galaxy S22", Category = "Electronics", Price = 899, Quantity = 40 }, new Product { Id = 3, Name = "Sony WH-1000XM4 Headphones", Category = "Electronics", Price = 349, Quantity = 30 }, new Product { Id = 4, Name = "Nike Air Zoom Pegasus", Category = "Footwear", Price = 120, Quantity = 100 }, new Product { Id = 5, Name = "Adidas Ultraboost", Category = "Footwear", Price = 180, Quantity = 80 }, new Product { Id = 6, Name = "Organic Apples (1kg)", Category = "Groceries", Price = 4, Quantity = 200 }, new Product { Id = 7, Name = "Organic Bananas (1 Dozen)", Category = "Groceries", Price = 3, Quantity = 150 } }; // Instruct EF Core to seed this data into the 'Products' table modelBuilder.Entity<Product>().HasData(initialProducts); } // This DbSet maps to a Products table in the database public DbSet<Product> Products { get; set; } } }
Configure Connection String and Redis Cache Settings:
Next, please modify the appsettings.json file as follows. Here, the Entity Framework Core will create the database named ProductsDB if it has not already been created in the SQL server. Also, we have added a section specifying the Redis connection details (host and port) and an instance name (prefix) to avoid key collisions. Please remember that 6379 is the Port number on which our Redis Server is running.
{ "ConnectionStrings": { "DefaultConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=ProductsDB;Trusted_Connection=True;TrustServerCertificate=True;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "RedisCacheOptions": { "Configuration": "localhost:6379", "InstanceName": "RedisCachingDemo" } }
RedisCacheOptions
This is the section under which all Redis-related configurations are specified. We can name this section anything.
- “Configuration”: “localhost:6379”: This key specifies the connection string for the Redis server. Here, localhost is the host where the Redis server is running. localhost also indicates that Redis is running on the same machine as your application. If Redis were hosted elsewhere, we would specify the IP address or hostname of that server. 6379 is the port number on which the Redis server listens for incoming connections.
- “InstanceName”: “RedisCachingDemo”: The InstanceName is a prefix used for all entries created by our application’s instance. This can be useful when multiple applications use the same Redis server, and we want to avoid key collisions and make it easier to identify which ones belong to which application. In this case, every key stored in Redis by this application will be prefixed with RedisCachingDemo.
Configure the DbContext and Redis Cache in the Program Class:
Next, we need to configure our application to support Redis cache, and for this purpose, we need to register the AddStackExchangeRedisCache service. So, please modify the Program class as follows. The following code is self-explained, so please read the comment lines for a better understanding.
using Microsoft.EntityFrameworkCore; using RedisCachingDemo.Data; using StackExchange.Redis; namespace RedisCachingDemo { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers() .AddJsonOptions(options => { // This will use the property names as defined in the C# model options.JsonSerializerOptions.PropertyNamingPolicy = null; }); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // Configure DbContext to Use SQL Server Database builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // Register Redis distributed cache builder.Services.AddStackExchangeRedisCache(options => { //This property is set to specify the connection string for Redis //The value is fetched from the application's configuration system, i.e., appsettings.json file options.Configuration = builder.Configuration["RedisCacheOptions:Configuration"]; //This property helps in setting a logical name for the Redis cache instance. //The value is also fetched from the appsettings.json file options.InstanceName = builder.Configuration["RedisCacheOptions:InstanceName"]; }); // Register the Redis connection multiplexer as a singleton service // This allows the application to interact directly with Redis for advanced scenarios builder.Services.AddSingleton<IConnectionMultiplexer>(sp => // Establish a connection to the Redis server using the configuration from appsettings.json ConnectionMultiplexer.Connect(builder.Configuration["RedisCacheOptions:Configuration"])); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); } } }
Create and Apply Database Migration:
Open the Package Manager Console and execute the Add-Migration command to create a new Migration file. Then, execute the Update-Database command to apply the migration to the database as follows.
Once you execute the above command, it should create the ProductsDB database with the Products table, as shown in the below image:
How to Use Redis for Cache in ASP.NET Core Web API:
Once the Redis server settings are configured in the Program class, we can inject the IDistributedCache service instance into our controllers or services to interact with Redis. The IDistributedCache interface provides GetStringAsync, SetStringAsync, and RemoveAsync methods in ASP.NET Core when Redis is used as the underlying cache provider.
SetStringAsync:
This method stores a string value in the cache with an associated key and optional caching options (such as expiration policies). When we call SetStringAsync, it converts our string data into a byte array and stores it in Redis under the specified key.
This method is typically used to cache data after fetching it from a database so subsequent requests can retrieve it faster. The method performs the write operation asynchronously, ensuring the main thread isn’t blocked while waiting for Redis to store the data. The syntax is given below:
How It Works:
- We need to call SetStringAsync(key, value, options) to cache data.
- The method serializes the string value (if required) and sends it to Redis to be stored.
- The DistributedCacheEntryOptions allows us to set parameters like absolute or sliding expiration. This means the cached item can be automatically removed after a set duration or if it hasn’t been accessed for a specified period.
- The cache is updated with the new value if the key already exists.
GetStringAsync:
This method retrieves a cached value (stored as a string) associated with the provided key. We need to use this method to quickly check if the data is available in the cache before querying a slower data source like a database. After retrieval, we must deserialize the string to its original object type. The syntax is given below:
How It Works:
- When we call GetStringAsync(key), it sends an asynchronous request to the Redis server to fetch the value associated with the key.
- If the key exists, it returns the corresponding string value (typically serialized data like JSON).
- If the key is not found (a cache miss), it returns null.
RemoveAsync:
This method deletes the cache entry in Redis based on the specified key. We need to use this method to maintain cache consistency. For instance, if a product is updated or deleted, remove its cache entry so that the next read operation fetches fresh data. The syntax is given below:
How It Works:
- When you call RemoveAsync(key), it instructs Redis to delete the data associated with the provided key. The removal is performed asynchronously, allowing your application to continue processing while the cache entry is being deleted.
- This is useful for cache invalidation when the underlying data has changed (e.g., after a database update or delete operation).
Using Redis Cache in ASP.NET Core Web API Controller:
To better understand, please create a new API Empty controller named ProductsController within the Controllers folder and copy and paste the following code. The following code is self-explained, so please read the comment lines for a better understanding.
using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Distributed; using RedisCachingDemo.Data; using RedisCachingDemo.Models; using System.Text.Json; namespace RedisCachingDemo.Controllers { [Route("api/[controller]")] [ApiController] public class ProductsController : ControllerBase { private readonly ApplicationDbContext _context; private readonly IDistributedCache _cache; // Inject ApplicationDbContext and IDistributedCache via constructor. public ProductsController(ApplicationDbContext context, IDistributedCache cache) { _context = context; _cache = cache; } // GET: api/products/all [HttpGet("all")] public async Task<IActionResult> GetAll() { var cacheKey = "GET_ALL_PRODUCTS"; List<Product> products; try { // Attempt to retrieve the product list from Redis cache. var cachedData = await _cache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedData)) { // Deserialize JSON string back to List<Product>. products = JsonSerializer.Deserialize<List<Product>>(cachedData) ?? new List<Product>(); } else { // Cache miss: fetch products from the database. products = await _context.Products.AsNoTracking().ToListAsync(); if (products != null) { // Serialize the product list to a JSON string. var serializedData = JsonSerializer.Serialize(products); // Define cache options (using sliding expiration). var cacheOptions = new DistributedCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(5)); // Store the serialized data in Redis. await _cache.SetStringAsync(cacheKey, serializedData, cacheOptions); } } return Ok(products); } catch (Exception ex) { // Return a 500 response if any error occurs. return StatusCode(500, new { message = "An error occurred while retrieving products.", details = ex.Message }); } } // GET: api/products/Category?Category=Fruits [HttpGet("Category")] public async Task<IActionResult> GetProductByCategory(string Category) { // Cache key includes the category to ensure unique cache entries. var cacheKey = $"PRODUCTS_{Category}"; List<Product> products; try { var cachedData = await _cache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedData)) { products = JsonSerializer.Deserialize<List<Product>>(cachedData) ?? new List<Product>(); } else { // Cache miss: fetch from the database by matching category. products = await _context.Products .Where(prd => prd.Category.ToLower() == Category.ToLower()) .AsNoTracking() .ToListAsync(); if (products != null) { var serializedData = JsonSerializer.Serialize(products); // Use absolute expiration so that the cache entry expires after 5 minutes. var cacheOptions = new DistributedCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromMinutes(5)); await _cache.SetStringAsync(cacheKey, serializedData, cacheOptions); } } return Ok(products); } catch (Exception ex) { return StatusCode(500, new { message = "An error occurred while retrieving products.", details = ex.Message }); } } // GET: api/products/{id} [HttpGet("{id}")] public async Task<IActionResult> GetProduct(int id) { // Cache key for a single product. var cacheKey = $"Product_{id}"; Product? product; try { var cachedData = await _cache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedData)) { product = JsonSerializer.Deserialize<Product>(cachedData) ?? new Product(); } else { // Fetch from database if not present in cache. product = await _context.Products.FindAsync(id); if (product == null) return NotFound($"Product with ID {id} not found."); var serializedData = JsonSerializer.Serialize(product); await _cache.SetStringAsync(cacheKey, serializedData, new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(5) }); } return Ok(product); } catch (Exception ex) { return StatusCode(500, new { message = "An error occurred while retrieving the product.", details = ex.Message }); } } // PUT: api/products/{id} [HttpPut("{id}")] public async Task<IActionResult> UpdateProduct(int id, [FromBody] Product updatedProduct) { if (id != updatedProduct.Id) { return BadRequest("Product ID mismatch."); } try { var existingProduct = await _context.Products.FindAsync(id); if (existingProduct == null) return NotFound($"Product with ID {id} not found."); // Update product details in the database. _context.Entry(existingProduct).CurrentValues.SetValues(updatedProduct); await _context.SaveChangesAsync(); // Update the cache for this product. var cacheKey = $"Product_{id}"; var serializedData = JsonSerializer.Serialize(updatedProduct); await _cache.SetStringAsync(cacheKey, serializedData, new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(5) }); return Ok(); } catch (Exception ex) { return StatusCode(500, new { message = "An error occurred while updating the product.", details = ex.Message }); } } // DELETE: api/products/{id} [HttpDelete("{id}")] public async Task<IActionResult> DeleteProduct(int id) { try { var product = await _context.Products.FindAsync(id); if (product == null) return NotFound($"Product with ID {id} not found."); // Remove product from the database. _context.Products.Remove(product); await _context.SaveChangesAsync(); // Remove product from the cache. var cacheKey = $"Product_{id}"; await _cache.RemoveAsync(cacheKey); return Ok(); } catch (Exception ex) { return StatusCode(500, new { message = "An error occurred while deleting the product.", details = ex.Message }); } } } }
Testing
Run the application and test that the Redis Cache is working as expected. Let’s try to access the API endpoint in Postman. The first request will take slightly longer to execute, but subsequent requests will improve the response time considerably. For me, it came down to just 7 milliseconds, which is quite impressive.
Redis Cache Controller for Cache Management
This controller will demonstrate advanced cache management, such as retrieving all cached keys, fetching a specific cache entry, and clearing cache entries. Now, we want to implement the following functionalities in our ASP.NET Core Web API Application:
- Retrieve All Cached Keys and Values: This is useful for debugging and monitoring cache usage to ensure efficient use of Redis.
- Retrieve a Specific Cache Entry by Key: Helps retrieve data directly for validation or manual processing.
- Clear All Cache Entries: Used for scenarios like application resets, major updates, or cache invalidation.
- Clear a Specific Cache Entry: Essential for maintaining cache consistency when only specific data changes.
Let us create a controller to demonstrate the above cache management operations. So, create a new API Empty Controller named RedisCacheController within the Controllers folder, and then copy and paste the following code:
using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Distributed; using StackExchange.Redis; namespace RedisCachingDemo.Controllers { [Route("api/[controller]")] [ApiController] public class RedisCacheController : ControllerBase { // Dependency for interacting with Redis via IDistributedCache private readonly IDistributedCache _distributedCache; // Dependency for interacting directly with Redis Server private readonly IConnectionMultiplexer _redisConnection; // Dependency for interacting with the configuration files like appsettings.json file private readonly IConfiguration _configuration; // Inject IDistributedCache, IConnectionMultiplexer, and IConfiguration public RedisCacheController(IDistributedCache distributedCache, IConnectionMultiplexer redisConnection, IConfiguration configuration) { _distributedCache = distributedCache; _redisConnection = redisConnection; _configuration = configuration; } // GET: api/RedisCache/all // Retrieve all keys and their values from the Redis cache [HttpGet("all")] public async Task<IActionResult> GetAllCachedKeysAndValues() { try { // Get the first available Redis server from the connection multiplexer var server = _redisConnection.GetServer(_redisConnection.GetEndPoints().First()); // Retrieve all keys from Redis var keys = server.Keys().ToArray(); // Read the instance name prefix from configuration string instanceName = _configuration["RedisCacheOptions:InstanceName"] ?? string.Empty; // Initialize a list to store key-value pairs from the cache var cacheEntries = new List<KeyValuePair<string, string>>(); // Loop through each key, remove the instance prefix and fetch the cached value foreach (var key in keys) { // Remove the instance name prefix if it exists var keyWithoutPrefix = key.ToString().Replace($"{instanceName}", ""); // Get the value associated with the key from the distributed cache var value = await _distributedCache.GetStringAsync(keyWithoutPrefix); // Add the key-value pair to the list; if value is null, set it to "null" cacheEntries.Add(new KeyValuePair<string, string>(keyWithoutPrefix, value ?? "null")); } // Return the list of key-value pairs as a JSON response return Ok(cacheEntries); } catch (Exception ex) { return StatusCode(500, new { message = "Failed to retrieve cache entries.", error = ex.Message }); } } // GET: api/RedisCache/{key} // Retrieve a specific cache entry by key [HttpGet("{key}")] public async Task<IActionResult> GetCacheEntryByKey(string key) { try { // Attempt to retrieve the value associated with the provided key asynchronously var value = await _distributedCache.GetStringAsync(key); // If the key is not found in the cache, return a 404 Not Found response if (value == null) { return NotFound(new { message = "Cache entry not found." }); } // Return the key-value pair as a JSON response return Ok(new { Key = key, Value = value }); } catch (Exception ex) { return StatusCode(500, new { message = "Failed to retrieve cache entry.", error = ex.Message }); } } // DELETE: api/RedisCache/all // Clear all cache entries [HttpDelete("all")] public IActionResult ClearAllCacheEntries() { try { // Get the first Redis server instance from the connection multiplexer var server = _redisConnection.GetServer(_redisConnection.GetEndPoints().First()); // Iterate over all keys in the Redis database foreach (var key in server.Keys()) { // Get the InstanceName from the configuration (as specified in appsettings.json) string instanceName = _configuration["RedisCacheOptions:InstanceName"] ?? string.Empty; // Remove the instance name prefix if it exists var keyWithoutPrefix = key.ToString().Replace($"{instanceName}", ""); // Remove each key-value pair from the distributed cache _distributedCache.Remove(keyWithoutPrefix); } // Return a success message return Ok(new { message = "All cache entries cleared." }); } catch (Exception ex) { return StatusCode(500, new { message = "Failed to clear cache entries.", error = ex.Message }); } } // DELETE: api/RedisCache/{key} // Clear a specific cache entry by key [HttpDelete("{key}")] public async Task<IActionResult> ClearCacheEntryByKey(string key) { try { // Remove the cache entry associated with the provided key asynchronously await _distributedCache.RemoveAsync(key); // Return a success message indicating the specific cache entry was cleared return Ok(new { message = $"Cache entry '{key}' cleared." }); } catch (Exception ex) { return StatusCode(500, new { message = "Failed to clear cache entry.", error = ex.Message }); } } } }
This controller provides endpoints to inspect the current cache (all keys/values), fetch an individual entry, and remove entries, all of which are useful for debugging and maintenance purposes. Now, run the application and test the functionalities, and it should work as expected.
What are the Benefits of using Redis Distributed Cache in ASP.NET Core Web API?
Using Redis as a distributed cache in an ASP.NET Core Web API can provide numerous benefits that enhance performance, scalability, and reliability. Some key advantages include:
- High-Speed Data Access: Redis is an in-memory data store, so data retrieval is exceptionally fast compared to fetching it from a traditional database. This helps improve the overall response times of your APIs, providing a better user experience.
- Scalability and Load Balancing: By storing frequently accessed data in a centralized Redis instance, multiple application servers can share the same cached data. This allows us to scale horizontally, adding more servers as needed without each server having to maintain its own cache. The centralized approach helps maintain consistency and efficiency, especially in load-balanced environments.
- Reduced Database Load: Serving data from the Redis cache reduces the frequency of database queries. This not only reduces the load on the database but also frees up database resources for other operations, improving the overall stability and performance of the system.
- Fault Tolerance: Unlike in-memory caching on individual servers, Redis caches are not tied to a specific application instance. If one application server goes down, the data in Redis remains intact and accessible to other servers. This separation increases fault tolerance and ensures a higher level of availability.
- Flexible Caching Options: Redis supports advanced data structures (e.g., strings, hashes, lists, sets) and provides more control over how data is cached and expired. This flexibility allows developers to use caching strategies to meet specific use cases, such as sliding expiration policies or preloading cache with frequently requested data.
- Improved Performance in High-Traffic Scenarios: For APIs that experience heavy traffic or have endpoints that need to deliver faster responses, Redis can handle a large volume of read-and-write operations with minimal overhead. This ensures consistent performance even under high load conditions.
Using Redis as a distributed cache in ASP.NET Core Web API enhances performance through rapid data retrieval, reduces the load on the database by serving repeated requests from memory, scales easily across multiple servers, and offers high availability. Additionally, flexible expiration policies and support for rich data types make Redis a robust choice for modern web applications.
In the next article, I will discuss the Difference Between In-Memory Caching and Distributed Caching in ASP.NET Core Applications. In this article, I explain How to Implement Redis Cache in ASP.NET Core Web API with Examples. I hope you enjoy this article, How to Implement Redis Cache in ASP.NET Core Web API.