R3Polska.Cache.Redis 1.1.0

dotnet add package R3Polska.Cache.Redis --version 1.1.0
                    
NuGet\Install-Package R3Polska.Cache.Redis -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="R3Polska.Cache.Redis" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="R3Polska.Cache.Redis" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="R3Polska.Cache.Redis" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add R3Polska.Cache.Redis --version 1.1.0
                    
#r "nuget: R3Polska.Cache.Redis, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package R3Polska.Cache.Redis@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=R3Polska.Cache.Redis&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=R3Polska.Cache.Redis&version=1.1.0
                    
Install as a Cake Tool

RedisCache

CI codecov License .NET

A lightweight, type-safe Redis cache wrapper for .NET that simplifies object storage with automatic JSON serialization/deserialization, batch operations, and flexible expiration policies.

Features

  • πŸš€ Simple API - Clean, intuitive interface for cache operations
  • πŸ”„ Automatic Serialization - Seamless JSON serialization/deserialization of complex objects
  • πŸ“¦ Batch Operations - Execute multiple Redis operations atomically
  • ⏰ Flexible Expiration - Support for both absolute and sliding expiration
  • πŸ“‹ List Support - Manage Redis lists with ease
  • πŸ”’ Type-Safe - Full generic support for compile-time type checking
  • πŸ§ͺ Well Tested - Comprehensive unit and BDD test coverage

Installation

dotnet add package R3Polska.Cache.Redis --version 1.0.0

Or add to your .csproj file:

<PackageReference Include="R3Polska.Cache.Redis" Version="1.0.0" />

Quick Start

Basic Setup

using R3Polska.Cache.Redis;
using R3Polska.Cache.Redis.Contract;
using StackExchange.Redis;

// Create Redis connection
var redis = ConnectionMultiplexer.Connect("localhost:6379");
ICache cache = new RedisCache(redis);

Simple Get/Set Operations

// Set a string value with expiration
await cache.SetAsync("user:name", "John Doe", TimeSpan.FromHours(1));

// Get a string value
var name = await cache.GetAsync<string>("user:name");

// Set a complex object
var user = new User 
{ 
    Id = 1, 
    Name = "John Doe", 
    Email = "john@example.com" 
};
await cache.SetAsync("user:1", user, TimeSpan.FromMinutes(30));

// Get the object back
var cachedUser = await cache.GetAsync<User>("user:1");

TryGet Pattern

// Check if key exists and get value in one operation
var (found, value) = await cache.TryGetValueAsync<string>("user:name");
if (found)
{
    Console.WriteLine($"Found: {value}");
}
else
{
    Console.WriteLine("Key not found");
}

Advanced Expiration Options

// Absolute expiration - expires at a specific time
var options = new RedisCacheEntryOptions
{
    AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(2)
};
await cache.SetAsync("session:token", token, options);

// Sliding expiration - resets on each access
var slidingOptions = new RedisCacheEntryOptions
{
    SlidingExpiration = TimeSpan.FromMinutes(15)
};
await cache.SetAsync("active:session", sessionData, slidingOptions);

// Combined expiration
var combinedOptions = new RedisCacheEntryOptions
{
    AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(24),
    SlidingExpiration = TimeSpan.FromMinutes(30)
};
await cache.SetAsync("user:preferences", prefs, combinedOptions);

Batch Operations

Execute multiple operations atomically:

await cache.ExecuteBatchAsync(batch =>
{
    batch.SetAsync("user:1:name", "Alice");
    batch.SetAsync("user:2:name", "Bob");
    batch.SetAsync("user:3:name", "Charlie");
});

// Batch operations with results
var tasks = new List<Task<string?>>();
await cache.ExecuteBatchAsync(batch =>
{
    tasks.Add(batch.GetAsync<string>("user:1:name"));
    tasks.Add(batch.GetAsync<string>("user:2:name"));
    tasks.Add(batch.GetAsync<string>("user:3:name"));
});

var names = await Task.WhenAll(tasks);

Working with Lists

// Add items to a list (left push)
await cache.LeftPushAsync("notifications:user:1", notification1);
await cache.LeftPushAsync("notifications:user:1", notification2);

// Add items to the right
await cache.RightPushAsync("queue:tasks", task1);

// Get list range
var notifications = await cache.GetRangeAsync<Notification>(
    "notifications:user:1", 
    start: 0, 
    stop: 9  // Get first 10 items
);

// Get specific list item
var firstNotification = await cache.GetListItemAsync<Notification>(
    "notifications:user:1", 
    index: 0
);

// Get list length
var count = await cache.GetListLengthAsync("notifications:user:1");

Removing Keys

// Remove a single key
var removed = await cache.RemoveAsync("old:key");

// Check if key exists
var exists = await cache.KeyExistsAsync("user:1");

API Reference

ICache Interface

Core Operations
  • Task<T?> GetAsync<T>(string key) - Get value by key
  • Task<(bool Found, T? Value)> TryGetValueAsync<T>(string key) - Try get with existence check
  • Task SetAsync<T>(string key, T value, TimeSpan? expiration = null) - Set value with optional expiration
  • Task SetAsync<T>(string key, T value, RedisCacheEntryOptions options) - Set value with advanced options
  • Task<bool> RemoveAsync(string key) - Remove key
  • Task<bool> KeyExistsAsync(string key) - Check if key exists
List Operations
  • Task LeftPushAsync<T>(string key, T value) - Push to list start
  • Task RightPushAsync<T>(string key, T value) - Push to list end
  • Task<IEnumerable<T>> GetRangeAsync<T>(string key, long start = 0, long stop = -1) - Get list range
  • Task<T?> GetListItemAsync<T>(string key, long index) - Get item at index
  • Task<long> GetListLengthAsync(string key) - Get list length
Batch Operations
  • Task ExecuteBatchAsync(Action<ICacheBatch> batchOperations) - Execute operations atomically

RedisCacheEntryOptions

Configure cache entry expiration:

public class RedisCacheEntryOptions
{
    public DateTimeOffset? AbsoluteExpiration { get; set; }
    public TimeSpan? SlidingExpiration { get; set; }
}

Examples

Caching API Responses

public async Task<WeatherForecast> GetWeatherAsync(string city)
{
    var cacheKey = $"weather:{city}";
    
    var (found, forecast) = await cache.TryGetValueAsync<WeatherForecast>(cacheKey);
    if (found)
    {
        return forecast!;
    }
    
    // Fetch from API
    var freshForecast = await weatherApi.GetForecastAsync(city);
    
    // Cache for 1 hour
    await cache.SetAsync(cacheKey, freshForecast, TimeSpan.FromHours(1));
    
    return freshForecast;
}

Session Management

public async Task<Session?> GetSessionAsync(string sessionId)
{
    var options = new RedisCacheEntryOptions
    {
        // Session expires after 8 hours
        AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(8),
        // Reset expiration on each access (15 min idle timeout)
        SlidingExpiration = TimeSpan.FromMinutes(15)
    };
    
    var session = await cache.GetAsync<Session>($"session:{sessionId}");
    
    if (session != null)
    {
        // Refresh sliding expiration
        await cache.SetAsync($"session:{sessionId}", session, options);
    }
    
    return session;
}

Activity Feed with Lists

public async Task AddActivityAsync(string userId, Activity activity)
{
    var key = $"feed:{userId}";
    
    // Add new activity to the front
    await cache.LeftPushAsync(key, activity);
    
    // Keep only last 50 activities (would need custom Redis command)
    // For simplicity, set expiration on the list
    await cache.SetAsync(key, activity, TimeSpan.FromDays(7));
}

public async Task<IEnumerable<Activity>> GetRecentActivitiesAsync(
    string userId, 
    int count = 20)
{
    var key = $"feed:{userId}";
    return await cache.GetRangeAsync<Activity>(key, 0, count - 1);
}

Requirements

  • .NET 9.0 or later
  • Redis server (or Valkey compatible)
  • StackExchange.Redis 2.10.1+

Building from Source

# Clone the repository
git clone https://github.com/recomaty/redis-cache.git
cd redis-cache

# Build the project
dotnet build

# Run unit tests
dotnet test RedisCache.Tests/RedisCache.Tests.csproj

# Run BDD tests (requires Redis/Valkey)
dotnet test RedisCache.BddTests/RedisCache.BddTests.csproj

# Generate coverage report
make test-coverage

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.

Authors

  • IDCT Bartosz PachoΕ‚ek
  • R3 Polska Sp. z o.o.

Acknowledgments

Built on top of the excellent StackExchange.Redis library.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 93 1/24/2026
1.0.0 94 1/23/2026