Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 59

Connecting to Azure Cache for Redis with C#

Recommended resources

Hello! Welcome back to our journey on optimizing Azure applications.

In the previous lesson, you successfully provisioned and configured your first Azure Cache for Redis instance. You now have a running cache in the cloud and its connection string at the ready. This is the infrastructure foundation we need.

Today, we move from infrastructure to application code. This lesson directly addresses the learning outcome: Connect to Azure Cache for Redis from a .NET application using the StackExchange.Redis library. You will leverage your strong C# and .NET background to write the code that brings your cache to life, enabling you to set, retrieve, and manage cached data. This is a critical hands-on step that paves the way for implementing advanced caching patterns.

1. The Core Component: StackExchange.Redis

To interact with Redis from a .NET application, we use a client library. The industry standard and most recommended library is StackExchange.Redis, developed and used by the team at Stack Overflow.

The central object in this library is the ConnectionMultiplexer. It is a powerful and highly optimized component that manages the network connections to your Redis server. Here are the two most important things to know about it:

  1. Thread-Safe: It is designed to be safely used by multiple threads concurrently.
  2. Singleton Pattern: It is intended to be created once at application startup and reused throughout the application's lifetime. Creating a new ConnectionMultiplexer for each operation is an anti-pattern that leads to poor performance and connection issues.

Therefore, we will register it as a singleton instance in our application's dependency injection container.

Redis Cache Interaction with Example C# Code
This diagram illustrates the role of Redis as an intermediary cache between a client application and a database. The C# code snippet shows the fundamental connection and set operation using the `StackExchange.Redis` library.

2. Setting Up the Connection

Let's begin by seeing how to add the library to a project and configure the ConnectionMultiplexer. Your experience with dependency injection in .NET Core will make this process very familiar.

The following video provides an excellent demonstration of this initial setup.

How to effectively use Redis Cache in .NET Core (Framework, Standard)

Watch this clip from the video 'How to effectively use Redis Cache in .NET Core' by Nick Chapsas. It clearly demonstrates installing the correct NuGet package and registering the IConnectionMultiplexer as a singleton in Program.cs.

Please watch from 02:28 to 04:29. Focus on these key steps: Installing the StackExchange.Redis NuGet package. Registering IConnectionMultiplexer as a singleton service. Using ConnectionMultiplexer.Connect() with a connection string retrieved from configuration.

As you saw in the video, the setup involves two main parts:

  1. Adding the StackExchange.Redis NuGet package to your project.
  2. In Program.cs, adding the service registration:
    builder.Services.AddSingleton<IConnectionMultiplexer>(sp => 
    {
        var configuration = sp.GetRequiredService<IConfiguration>();
        var connectionString = configuration.GetConnectionString("Redis"); // Or another config key
        return ConnectionMultiplexer.Connect(connectionString);
    });
    

This ensures a single, shared instance of the ConnectionMultiplexer is available throughout your application.


3. Building a Reusable Cache Service

While you could use the IConnectionMultiplexer directly in your controllers, it's a best practice to create an abstraction. We'll build a dedicated cache service to encapsulate the logic for getting, setting, and removing data. This aligns with the Single Responsibility Principle and makes your code cleaner, more maintainable, and easier to test.

Our service will handle a crucial detail: serialization. Redis stores data as strings (or raw bytes). To store complex .NET objects (like a WeatherForecast object), you must first serialize them into a format like JSON. When you retrieve the data, you must then deserialize it back into a .NET object.

The following video provides a comprehensive, step-by-step walkthrough of building such a service.

How Azure Redis Cache can make your .NET 7 API Super Fast!

This video, 'How Azure Redis Cache can make your .NET 7 API Super Fast!' by Israel Quiroz, provides a complete implementation of a generic cache service. It's an excellent, practical guide.

Please watch the segment from 02:57 to 08:29. Follow along as the presenter builds the caching layer. Pay close attention to: NuGet Package (02:57 - 03:16): Confirms the installation of StackExchange.Redis. Interface and Class (03:16 - 04:44): The creation of IRedisCache and RedisCache to define a contract for caching operations. Method Implementation (04:44 - 07:23): The detailed implementation of GetCacheData, SetCacheData, and RemoveData. Note how System.Text.Json (or Newtonsoft.Json) is used for serialization and deserialization, and how IDatabase is obtained from the ConnectionMultiplexer. DI Registration (07:23 - 08:29): Registering both the ConnectionMultiplexer singleton and the scoped IRedisCache service in Program.cs.


4. Implementing the Cache Service in Your Project

Let's consolidate the concepts from the videos into a concrete implementation. You will now apply this to a .NET Web API project.

Step 1: Project and Configuration

  1. Create a new ASP.NET Core Web API project in Visual Studio.

  2. Install the StackExchange.Redis package via the NuGet Package Manager.

  3. In your appsettings.json file, add the primary connection string you saved from the previous lesson.

    {
      "ConnectionStrings": {
        "Redis": "your-cache-name.redis.cache.windows.net:6380,password=YOUR_ACCESS_KEY,ssl=True,abortConnect=False"
      },
      // ... other settings
    }
    

Step 2: The Interface and Service Class

Create a new folder named Services. Inside it, create the interface and the class for your cache service.

ICacheService.cs

public interface ICacheService
{
    Task<T> GetData<T>(string key);
    Task<bool> SetData<T>(string key, T value, DateTimeOffset expirationTime);
    Task<bool> RemoveData(string key);
}

CacheService.cs
This implementation uses System.Text.Json for serialization.

using StackExchange.Redis;
using System.Text.Json;

public class CacheService : ICacheService
{
    private readonly IDatabase _db;

    public CacheService(IConnectionMultiplexer multiplexer)
    {
        _db = multiplexer.GetDatabase();
    }

    public async Task<T> GetData<T>(string key)
    {
        var value = await _db.StringGetAsync(key);
        if (!string.IsNullOrEmpty(value))
        {
            return JsonSerializer.Deserialize<T>(value);
        }
        return default;
    }

    public async Task<bool> SetData<T>(string key, T value, DateTimeOffset expirationTime)
    {
        TimeSpan expiryTime = expirationTime.DateTime.Subtract(DateTime.Now);
        var isSet = await _db.StringSetAsync(key, JsonSerializer.Serialize(value), expiryTime);
        return isSet;
    }

    public async Task<bool> RemoveData(string key)
    {
        bool _isKeyExist = await _db.KeyExistsAsync(key);
        if (_isKeyExist)
        {
            return await _db.KeyDeleteAsync(key);
        }
        return false;
    }
}

Step 3: Dependency Injection in Program.cs

Register your services.

using StackExchange.Redis;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Register Redis ConnectionMultiplexer as a Singleton
builder.Services.AddSingleton<IConnectionMultiplexer>(sp => 
    ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("Redis"))
);

// Register the custom Cache Service
builder.Services.AddScoped<ICacheService, CacheService>();

var app = builder.Build();

// ... rest of Program.cs

Step 4: Using the Service in a Controller

Finally, inject ICacheService into a controller and use it. This example demonstrates a basic check-and-set logic.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly ICacheService _cacheService;
    private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly" };

    public WeatherForecastController(ICacheService cacheService)
    {
        _cacheService = cacheService;
    }

    [HttpGet(Name = "GetWeatherForecast")]
    public async Task<IEnumerable<WeatherForecast>> Get()
    {
        // Define a unique key for this data
        var cacheKey = "weatherForecast";
        
        // Try to get data from cache
        var cachedData = await _cacheService.GetData<IEnumerable<WeatherForecast>>(cacheKey);
        
        if (cachedData != null)
        {
            // Data found in cache, return it
            return cachedData;
        }

        // Data not in cache, generate it
        var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        }).ToArray();
        
        // Set the data in cache with an expiration time (e.g., 1 minute)
        var expirationTime = DateTimeOffset.Now.AddMinutes(1);
        await _cacheService.SetData(cacheKey, forecast, expirationTime);

        // Return the newly generated data
        return forecast;
    }
}

When you run this code and call the endpoint, the first request will generate the data and store it in your Azure Cache for Redis. Subsequent requests within one minute will retrieve the data directly from the cache, which will be significantly faster.


Conclusion

Excellent work! You have successfully bridged the gap between your Azure infrastructure and your .NET application. You now have the practical skills to connect to a Redis cache, manage the connection efficiently, and perform core data operations.

Key Takeaways:

  • The StackExchange.Redis library is the standard for connecting .NET applications to Redis.
  • Its core ConnectionMultiplexer object should always be managed as a long-lived singleton.
  • Creating a separate service to abstract cache operations is a robust design pattern that improves code quality.
  • Since Redis stores string data, complex .NET objects must be serialized (e.g., to JSON) before being stored and deserialized upon retrieval.

Next Steps

The code you wrote today is a foundational implementation of a very common caching strategy. In our next lesson, we will formalize this strategy by studying the cache-aside pattern. We'll discuss how it helps reduce database load and improve response times, and we will refine our code to clearly separate data source logic from caching logic.