Skip to content

simoneM93/AspNetCoreCacheKit

Repository files navigation

AspNetCoreCacheKit

NuGet Publish to NuGet License: MIT GitHub Sponsors Changelog

A lightweight caching library for ASP.NET Core featuring group-based keys, per-entry and per-group duration, configurable via appsettings.json, and a DI-ready design that wraps IMemoryCache in a clean, testable abstraction.

Why AspNetCoreCacheKit? IMemoryCache is powerful but low-level. AspNetCoreCacheKit adds group-based key management, per-group and per-entry expiration, appsettings.json configuration, and a consistent type-safe API that makes caching easy to use and easy to mock in tests.


✨ Features

  • 🔑 Group-based keys — organise cache entries with prefixes like "users:123"
  • GetOrCreate and GetOrCreateAsync — read-through pattern out of the box
  • ⏱️ Per-group and per-entry duration — fine-grained expiration control
  • 🗑️ Delete — remove a single entry by key or group + key
  • Configuration validation with DataAnnotations
  • 📐 Nullable reference types and generic type-safe Set<T> support
  • 🎛️ appsettings.json configuration with sensible defaults
  • 🧪 DI-ready — register with one line, mock ICacheService in tests

📋 Requirements

Requirement Minimum version
.NET 9.0+
ASP.NET Core 9.0+

🚀 Installation

dotnet add package AspNetCoreCacheKit

🎯 Quick Start

1. Configure appsettings.json

{
  "CacheOptions": {
    "IsEnabled": true,
    "DurationMinutes": 60,
    "GroupDurations": {
      "users": 30,
      "tokens": 5,
      "countries": 1440
    }
  }
}

All duration values are expressed in minutes. GroupDurations is optional — groups without an entry use the global DurationMinutes.

2. Register services

// With appsettings.json
builder.Services.AddAspNetCoreCacheKit(builder.Configuration);

// Without appsettings (uses defaults)
builder.Services.AddAspNetCoreCacheKit();

3. Inject and use

[ApiController]
public class UsersController : ControllerBase
{
    private readonly ICacheService _cache;

    public UsersController(ICacheService cache)
    {
        _cache = cache;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetUser(int id, CancellationToken ct)
    {
        // Uses GroupDurations["users"] = 30 min from appsettings
        var user = await _cache.GetOrCreateAsync(
            "users",
            id.ToString(),
            _ => GetUserFromDb(id),
            ct: ct);

        return user is null ? NotFound() : Ok(user);
    }
}

📚 API Reference

Duration priority

Every method that writes to the cache resolves the expiration following this priority chain — first wins:

  1. duration parameter passed explicitly to the method
  2. GroupDurations[groupKey] configured in appsettings.json
  3. Global DurationMinutes from appsettings.json
// 1. Explicit duration wins — cached for 2 minutes regardless of everything else
await _cache.GetOrCreateAsync("tokens", userId, _ => GenerateTokenAsync(), TimeSpan.FromMinutes(2));

// 2. No explicit duration — uses GroupDurations["users"] = 30 min from appsettings
await _cache.GetOrCreateAsync("users", "42", _ => GetUserFromDb(42));

// 3. No explicit duration, no group config — uses global DurationMinutes = 60 min
await _cache.GetOrCreateAsync("misc", "key", _ => LoadSomethingAsync());

GetOrCreateAsync — async read-through

// With group — uses GroupDurations["users"] or global fallback
var user = await _cache.GetOrCreateAsync("users", "42", _ => GetUserFromDb(42), ct: ct);

// With group + explicit duration override
var token = await _cache.GetOrCreateAsync(
    "tokens", userId,
    _ => GenerateTokenAsync(userId),
    duration: TimeSpan.FromMinutes(5),
    ct);

// Without group key
var config = await _cache.GetOrCreateAsync("app:config", _ => LoadConfigAsync(), ct: ct);

GetOrCreate — sync read-through

// Uses GroupDurations["countries"] = 1440 min from appsettings
var countries = _cache.GetOrCreate("countries", () => LoadCountries());

// Explicit override
var result = _cache.GetOrCreate("countries", "IT", () => LoadItaly(), TimeSpan.FromHours(2));

Set — explicit write

_cache.Set("users", "42", user);                                   // group duration or global
_cache.Set("users", "42", user, TimeSpan.FromMinutes(10));         // explicit override

Delete — remove an entry

_cache.Delete("users", "42");
_cache.Delete("app:config");

⚙️ Configuration options

Option Type Default Description
IsEnabled bool true Enables or disables caching entirely. When false, factories are always invoked.
DurationMinutes int 60 Global default cache duration in minutes.
GroupDurations Dictionary<string, int> {} Per-group durations in minutes. Key = group name, Value = duration in minutes.

Setting IsEnabled: false is useful in development or testing environments where you want to bypass the cache without changing code.


🧪 Testing

ICacheService is a plain interface — mock it directly in unit tests:

var cacheMock = new Mock<ICacheService>();

cacheMock
    .Setup(c => c.GetOrCreateAsync(
        "users", "1",
        It.IsAny<Func<ICacheEntry, Task<User>>>(),
        It.IsAny<TimeSpan?>(),
        It.IsAny<CancellationToken>()))
    .ReturnsAsync(new User { Id = 1, Name = "Simone" });

❤️ Support

If you find AspNetCoreCacheKit useful, consider sponsoring its development.

Sponsor simoneM93


📄 License

MIT — see LICENSE for details.

About

Lightweight, extensible caching toolkit for ASP.NET Core (.NET 9+) — providers, middleware and helpers for in-memory & distributed caching.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages