CacheGraph.Extensions 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package CacheGraph.Extensions --version 1.0.0
                    
NuGet\Install-Package CacheGraph.Extensions -Version 1.0.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="CacheGraph.Extensions" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CacheGraph.Extensions" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="CacheGraph.Extensions" />
                    
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 CacheGraph.Extensions --version 1.0.0
                    
#r "nuget: CacheGraph.Extensions, 1.0.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 CacheGraph.Extensions@1.0.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=CacheGraph.Extensions&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=CacheGraph.Extensions&version=1.0.0
                    
Install as a Cake Tool

CacheGraph πŸ§ πŸ”—

Cache-aside with dependency graph and cascade invalidation for .NET

CacheGraph combines the cache-aside pattern with a dependency graph that maps cache keys to business entities. When an entity changes, all cache entries that depend on it are automatically invalidated in cascade β€” no manual cache key management needed.

Unlike FusionCache, EasyCaching, or Microsoft HybridCache (which only support flat tag-based invalidation), CacheGraph models relationships as a traversable bidirectional graph, enabling true cascade invalidation across entity hierarchies.

πŸ“¦ Installation

dotnet add package CacheGraph.Core

For Microsoft.Extensions integration (DI, Logging, IMemoryCache, IDistributedCache):

dotnet add package CacheGraph.Extensions

✨ Features

  • Cache-aside with GetOrSetAsync β€” on-demand loading with factory and stampede protection
  • Dependency graph β€” bidirectional relationships between cache keys and entities
  • Cascade invalidation β€” invalidate all entries that depend on an entity, type, collection, predicate, or query
  • Stampede protection β€” distributed lock ensures only one thread executes the factory per key
  • 8 interfaces for extensibility (cache, graph, lock, events, telemetry, logging, serialization)
  • DI integration β€” AddCacheGraph() / AddCacheGraphWithLogging() for Microsoft.Extensions.DependencyInjection
  • Cache adapters β€” wrap IMemoryCache or IDistributedCache as ICacheProvider
  • Events β€” CacheSetEvent, CacheRemovedEvent, EntityInvalidatedEvent, QueryInvalidatedEvent
  • Cross-instance backplane β€” CacheGraphBackplane replicates invalidations between instances over the event provider (in-memory or Redis Pub/Sub) without re-publish loops
  • Declarative caching β€” [CacheResult] attribute + CacheProxy (Castle DynamicProxy) caches interface methods automatically and registers dependencies for cascade invalidation
  • Redis providers (CacheGraph.Redis) β€” distributed cache, graph storage, distributed locks, and pub/sub event invalidation with self-message filtering
  • Metrics β€” hits, misses, sets, removes, invalidations, operation times, graph stats
  • Thread-safe β€” all in-memory providers use lock-based writes and snapshot reads
  • Benchmarks β€” BenchmarkDotNet suite comparing CacheGraph vs manual cache-aside vs FusionCache
  • Multi-target β€” .NET 8, .NET 9, and .NET 10
  • Zero external dependencies (Core) β€” only the .NET BCL

πŸš€ Quick Start

Manual setup

using CacheGraph.Core;
using CacheGraph.Core.Implementaciones;
using CacheGraph.Core.Interfaces;
using CacheGraph.Core.Modelos;

var cacheGraph = new CacheGraph(
    graphProvider: new MemoryGraphProvider(),
    cacheProvider: new MemoryCacheProvider(),
    eventProvider: new MemoryEventProvider(),
    logger: new ConsoleLogger(),
    telemetryProvider: new NoOpTelemetryProvider(),
    distributedLock: new MemoryDistributedLock(),
    options: new GraphOptions
    {
        DefaultTtl = TimeSpan.FromMinutes(30),
        LockTimeout = TimeSpan.FromSeconds(30),
        LockRetryCount = 3,
    });

// Cache-aside with entity dependency
var product = await cacheGraph.GetOrSetAsync(
    "product:42",
    factory: () => _db.GetProductAsync(42),
    dependencies: new[] { "entity:Product:42" },
    ttl: TimeSpan.FromMinutes(5));

// When the entity changes, invalidate it β†’ product:42 is removed automatically
await cacheGraph.InvalidateEntityAsync<Product>("42");

// Invalidate all Product cache entries (by type)
await cacheGraph.InvalidateEntityTypeAsync<Product>();

// Invalidate a collection of entities
await cacheGraph.InvalidateCollectionAsync<Product>(new[] { "42", "43", "44" });

// Predicate-based invalidation
await cacheGraph.InvalidateByPredicateAsync<Product>(p => p.Discontinued);

// Query-based invalidation
await cacheGraph.InvalidateByQueryAsync<Product>(q => q.Where(p => p.Price < 10));

Dependency Injection setup

// Program.cs
builder.Services.AddCacheGraph(options =>
{
    options.DefaultTtl = TimeSpan.FromMinutes(30);
});

// With Microsoft.Extensions.Logging
builder.Services.AddCacheGraphWithLogging(options =>
{
    options.DefaultTtl = TimeSpan.FromMinutes(30);
});

// Fluent builder
builder.Services.AddCacheGraph(builder => builder
    .UseMemoryCache()
    .UseMicrosoftLogging()
    .WithDefaultTtl(TimeSpan.FromMinutes(30))
);

Using IMemoryCache or IDistributedCache as the cache provider

// Wrap IMemoryCache as ICacheProvider
var memoryCache = new MemoryCache(new MemoryCacheOptions());
var cacheProvider = new MemoryCacheProviderAdapter(memoryCache);

// Wrap IDistributedCache as ICacheProvider (uses JSON serialization)
var distributedCache = new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions()));
var cacheProvider = new DistributedCacheProviderAdapter(
    distributedCache,
    new JsonCacheSerializer());

Declarative caching with attributes (CacheGraph.Attributes)

using CacheGraph.Attributes;

public interface IProductService
{
    [CacheResult("products:{id}", 300, "entity:Product:{id}")]
    Task<Product> GetByIdAsync(int id);
}

// Manual wiring
var proxy = CacheProxy.Create<IProductService>(new ProductService(), cacheGraph);

// Or via DI
services.AddCacheGraph();
services.AddCacheGraphProxied<IProductService, ProductService>();

Keys support {paramName} placeholders; dependencies can be declared as templates and are also auto-detected from id/ids parameters (entity:{Type}:{value}). Cache hits skip the target; cascade invalidation works transparently.

Cross-instance backplane (CacheGraph.Backplane)

// Same event provider shared across instances (Redis Pub/Sub with CacheGraph.Redis)
services.AddCacheGraph();
services.AddCacheGraphBackplane();

// At startup β€” start replicating remote invalidations
var backplane = provider.GetRequiredService<CacheGraphBackplane>();
await backplane.StartAsync();

Invalidation events carry the affected keys, so every instance removes the same entries from its local cache and graph. Replication is silent β€” no re-publish loops. With Redis, messages from the publishing instance are filtered out automatically.

Benchmarks (CacheGraph.Benchmarks)

dotnet run --project CacheGraph.Benchmarks -c Release -- --filter '*GetOrSetBenchmarks*'

Covers hot/cold GetOrSetAsync throughput, cascade invalidation cost at 10/100/1000 dependents, and a head-to-head comparison against manual cache-aside and FusionCache.

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      ICacheGraph                         β”‚
β”‚                   (main faΓ§ade)                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ ICache     β”‚ IGraph       β”‚ IDistributed β”‚ IEvent         β”‚
β”‚ Provider   β”‚ Provider     β”‚ Lock         β”‚ Provider       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ ITelemetryProvider  β”‚  ICacheGraphLogger                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Edge direction: cache-key β†’ entity (the cache key depends on the entity).

product:42  ──────►  entity:Product:42  ◄──────  products:featured
   (dep)              (entity)                  (dep)

cart:user:42  ──────►  entity:User:42
   (dep)              (entity)

When entity:Product:42 is invalidated, the graph traverses incoming edges to find product:42 and products:featured, removing both from the cache.

πŸ“Š Interfaces

Interface Purpose
ICacheGraph Main faΓ§ade β€” cache-aside, invalidation, graph queries
ICacheProvider Key-value storage (get, set, remove, exists)
IGraphProvider Dependency graph (nodes, edges, traversal)
IDistributedLock Distributed lock for stampede protection
IEventProvider Publish/subscribe event system
ITelemetryProvider Activity tracking and performance telemetry
ICacheGraphLogger Logging abstraction
ICacheSerializer Serialization for distributed cache adapters

βš™οΈ Configuration

var options = new GraphOptions
{
    DefaultTtl       = TimeSpan.FromHours(1),   // Default cache entry TTL
    LockTimeout     = TimeSpan.FromSeconds(30), // Max wait to acquire lock
    LockRetryCount  = 3,                        // Lock acquisition retries
    LockRetryDelay  = TimeSpan.FromMilliseconds(100),
    EnableValidation = true,                     // Input validation
};

πŸ§ͺ Tests

dotnet test --framework net8.0

286 tests with xUnit + FluentAssertions, covering:

  • Unit tests for all providers, models, and the main CacheGraph faΓ§ade
  • DI integration tests
  • Cache adapter tests (IMemoryCache, IDistributedCache)
  • Concurrency stress tests (stampede protection, concurrent invalidation, concurrent edge addition, metrics accuracy under load)
  • Backplane tests (cross-instance invalidation replication, no re-publish loops)
  • Redis Pub/Sub tests (channel subscription, self-message filtering, unsubscribe)
  • Declarative caching tests (key templates, auto-dependency detection, invalidation)

🎯 Demo

dotnet run --project CacheGraph.Demo

πŸ“„ License

MIT License β€” see LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  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 is compatible.  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 (2)

Showing the top 2 NuGet packages that depend on CacheGraph.Extensions:

Package Downloads
CacheGraph.Redis

Redis providers for CacheGraph β€” distributed cache, graph storage, distributed locks, and pub/sub event invalidation

CacheGraph.EntityFrameworkCore

Automatic query caching, economic thresholds, and cascade invalidation for Entity Framework Core via CacheGraph dependency graph

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 67 8/21/2026
1.0.0 115 8/6/2026