Architecture - Code - Data

Redis integration tutorial – C# and Python – for Beginners

I will be demonstrating a “Cache-Aside” pattern, which is a basic Redis integration to an app. What it basically does is:

  1. Request Data
  2. Check if the requested data is present in Cache
  3. if cache is present, then return the cached data, else fetch from db and return

Find the GitHub code – https://github.com/ninethsense/code-share/tree/master/RedisSample

Prerequcites

  1. Docker (this is not mandatory but I love to keep by machine clean so I will be using Docker Desktop)
  2. Visual Studio or Visual Studio Code with .NET CLI installed.
  3. Python (optional, I will be demonstrating this as well in this same blog)

Step 1: Setup Docker with Redis

Use the below docker-compose.yml

version: '3.8'
services:
  redis:
    image: redis/redis-stack:latest
    container_name: redis-stack
    ports:
      - "6379:6379"  # The code connects here
      - "8001:8001"  # The visual UI (Redis Insight)

Next, you can create the image using command below:

docker-compose up -d

This is my command window looks like:

Additionally, you can verify on Docker Desktop if you use one

Optionally, you can check in the browser if Redis Dashboard is working – http://localhost:8001/

Step 2 – .NET Program

Create your .net project using Visual Studio Code or .NET CLI.

You require a dependency to be installed:

dotnet add package StackExchange.Redis

Below is the program for fetching data. Note that, I have not used any real DB but you are free to use any database such as SQLite or SQL Server.

using StackExchange.Redis;
using System.Diagnostics; // To measure speed

class Program
{
    // 1. Connection to Redis
    // "lazy" means we only connect when we actually need it.
    private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
    {
        return ConnectionMultiplexer.Connect("localhost:6379");
    });

    public static ConnectionMultiplexer Connection => lazyConnection.Value;

    static async Task Main(string[] args)
    {
        var db = Connection.GetDatabase();

        Console.WriteLine("--- Redis Cache-Aside Demo ---");
        Console.WriteLine("Enter a Product ID (e.g., 101) to fetch details.");
        Console.WriteLine("Type 'exit' to quit.\n");

        while (true)
        {
            Console.Write("Enter Product ID: ");
            string productId = Console.ReadLine();
            if (productId == "exit") break;

            string cacheKey = $"product:{productId}";
            Stopwatch sw = Stopwatch.StartNew();

            // STEP A: Check Redis Cache First
            string cachedValue = await db.StringGetAsync(cacheKey);

            if (!string.IsNullOrEmpty(cachedValue))
            {
                // HIT! Found in cache
                sw.Stop();
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine($"[CACHE HIT] Found: {cachedValue}");
                Console.WriteLine($"Time Taken: {sw.ElapsedMilliseconds}ms (Super Fast!)\n");
                Console.ResetColor();
            }
            else
            {
                // MISS! Not in cache, go to "Database"
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("[CACHE MISS] Not found in Redis. Fetching from Slow Database...");

                string dbValue = FetchFromSlowDatabase(productId); // This takes 2 seconds

                if (dbValue != null)
                {
                    // STEP B: Save to Redis for next time (Expires in 60 seconds)
                    await db.StringSetAsync(cacheKey, dbValue, TimeSpan.FromSeconds(60));

                    sw.Stop();
                    Console.WriteLine($"[DB READ] Found: {dbValue}");
                    Console.WriteLine($"Time Taken: {sw.ElapsedMilliseconds}ms (Slow...)\n");
                }
                else
                {
                    Console.WriteLine("Product does not exist in Database.\n");
                }
                Console.ResetColor();
            }
        }
    }

    // SIMULATED DATABASE
    // In a real app, this would be your SQLite/SQL Server call.
    static string FetchFromSlowDatabase(string id)
    {
        // Simulate a heavy query delay
        Thread.Sleep(2000);

        // Simple mock data
        return id switch
        {
            "101" => "iPhone 15 Pro",
            "102" => "Samsung Galaxy S24",
            "103" => "Sony Headphones",
            _ => null
        };
    }
}

Test Run

Bonus, Python implementation

Note that, there is no change in Redis setup here. Just create the python program, install dependencies, then execute.

pip install redis

Here is my RedisSample.py

import redis
import time
import sys

# 1. Connect to Redis
# decode_responses=True ensures we get strings back, not bytes
r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def fetch_from_slow_database(product_id):
    """Simulates a slow database query"""
    time.sleep(2)  # Simulate 2-second delay
    
    mock_db = {
        "101": "iPhone 15 Pro",
        "102": "Samsung Galaxy S24",
        "103": "Sony Headphones"
    }
    return mock_db.get(product_id)

def main():
    print("--- Redis Cache-Aside Demo (Python) ---")
    print("Enter a Product ID (e.g., 101). Type 'exit' to quit.\n")

    while True:
        product_id = input("Enter Product ID: ")
        if product_id.lower() == 'exit':
            break

        cache_key = f"product:{product_id}"
        
        # Start timing
        start_time = time.time()

        # STEP A: Check Redis Cache First
        cached_value = r.get(cache_key)

        if cached_value:
            # HIT! Found in cache
            elapsed = (time.time() - start_time) * 1000
            print(f"\033[92m[CACHE HIT] Found: {cached_value}")
            print(f"Time Taken: {elapsed:.2f}ms (Super Fast!)\033[0m\n")
        else:
            # MISS! Not in cache
            print("\033[93m[CACHE MISS] Not found in Redis. Fetching from Slow Database...\033[0m")
            
            db_value = fetch_from_slow_database(product_id)

            if db_value:
                # STEP B: Save to Redis (Expires in 60 seconds)
                # setex = Set with Expiration
                r.setex(cache_key, 60, db_value)

                elapsed = (time.time() - start_time) * 1000
                print(f"[DB READ] Found: {db_value}")
                print(f"Time Taken: {elapsed:.2f}ms (Slow...)\n")
            else:
                print("Product does not exist in Database.\n")

if __name__ == "__main__":
    main()

Run!

Leave a Reply