Skip to main content
Version: 2.4.1

Caching Examples

This page walks through the three caching examples included with RCommon: in-process memory cache, distributed memory cache, Redis distributed cache, and the persistence caching layer that caches repository query results.

Example Projects

Examples/Caching/
Examples.Caching.MemoryCaching/ — IMemoryCache + IDistributedCache (in-memory)
Examples.Caching.RedisCaching/ — IDistributedCache backed by Redis
Examples.Caching.PersistenceCaching/ — ICachingGraphRepository<T> with EF Core

Example 1: In-Memory and Distributed Memory Caching

Installation

NuGet Package
dotnet add package RCommon.MemoryCache
NuGet Package
dotnet add package RCommon.Json.JsonNet

Configuration

services.AddRCommon()
// Serialization is required for distributed cache (values are serialized to bytes)
.WithJsonSerialization<JsonNetBuilder>()
.WithMemoryCaching<InMemoryCachingBuilder>(cache =>
{
cache.Configure(x =>
{
x.ExpirationScanFrequency = TimeSpan.FromMinutes(1);
});
// Optionally cache dynamically compiled LINQ expression trees
cache.CacheDynamicallyCompiledExpressions();
})
.WithDistributedCaching<DistributedMemoryCacheBuilder>(cache =>
{
cache.Configure(x =>
{
x.ExpirationScanFrequency = TimeSpan.FromMinutes(1);
});
});

WithMemoryCaching<InMemoryCachingBuilder> registers IMemoryCache backed by Microsoft.Extensions.Caching.Memory.

WithDistributedCaching<DistributedMemoryCacheBuilder> registers IDistributedCache backed by an in-process distributed cache (useful for development and single-node deployments).

Application Service

Inject IMemoryCache and IDistributedCache as you would in any .NET application. RCommon's IJsonSerializer handles serialization for the distributed cache:

public class TestApplicationService : ITestApplicationService
{
private readonly IMemoryCache _memoryCache;
private readonly IDistributedCache _distributedCache;
private readonly IJsonSerializer _serializer;

public TestApplicationService(
IMemoryCache memoryCache,
IDistributedCache distributedCache,
IJsonSerializer serializer)
{
_memoryCache = memoryCache;
_distributedCache = distributedCache;
_serializer = serializer;
}

// IMemoryCache — objects stored by reference, in-process only
public void SetMemoryCache(string key, TestDto data)
=> _memoryCache.Set<TestDto>(key, data);

public TestDto GetMemoryCache(string key)
=> _memoryCache.Get<TestDto>(key);

// IDistributedCache — objects serialized to bytes, transport-agnostic
public void SetDistributedMemoryCache(string key, Type type, object data)
=> _distributedCache.Set(key,
Encoding.UTF8.GetBytes(_serializer.Serialize(data, type)));

public TestDto GetDistributedMemoryCache(string key)
{
var cache = _distributedCache.Get(key);
return _serializer.Deserialize<TestDto>(Encoding.UTF8.GetString(cache));
}
}

Usage

var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddRCommon()
.WithJsonSerialization<JsonNetBuilder>()
.WithMemoryCaching<InMemoryCachingBuilder>(cache =>
{
cache.Configure(x =>
{
x.ExpirationScanFrequency = TimeSpan.FromMinutes(1);
});
})
.WithDistributedCaching<DistributedMemoryCacheBuilder>(cache =>
{
cache.Configure(x =>
{
x.ExpirationScanFrequency = TimeSpan.FromMinutes(1);
});
});

services.AddTransient<ITestApplicationService, TestApplicationService>();
}).Build();

var appService = host.Services.GetRequiredService<ITestApplicationService>();

// Store and retrieve from IMemoryCache
appService.SetMemoryCache("test-key", new TestDto("test data 1"));
var testData1 = appService.GetMemoryCache("test-key");

// Store and retrieve from IDistributedCache
appService.SetDistributedMemoryCache("test-key", typeof(TestDto), new TestDto("test data 2"));
var testData2 = appService.GetDistributedMemoryCache("test-key");

Console.WriteLine(testData1.Message); // test data 1
Console.WriteLine(testData2.Message); // test data 2

Example 2: Redis Distributed Caching

Installation

NuGet Package
dotnet add package RCommon.RedisCache
NuGet Package
dotnet add package RCommon.Json.JsonNet

Configuration

Swap DistributedMemoryCacheBuilder for RedisCachingBuilder. The application service code does not change:

services.AddRCommon()
.WithJsonSerialization<JsonNetBuilder>()
.WithDistributedCaching<RedisCachingBuilder>(cache =>
{
cache.Configure(redis =>
{
// StackExchange.Redis ConfigurationOptions
redis.ConfigurationOptions = new ConfigurationOptions
{
EndPoints = { "localhost:6379" },
AbortOnConnectFail = false
};
});
});

Application Service (unchanged)

The application service injects IDistributedCache and IJsonSerializer. The same code that worked against the in-memory distributed cache works against Redis:

public class TestApplicationService : ITestApplicationService
{
private readonly IDistributedCache _distributedCache;
private readonly IJsonSerializer _serializer;

public TestApplicationService(
IDistributedCache distributedCache,
IJsonSerializer serializer)
{
_distributedCache = distributedCache;
_serializer = serializer;
}

public void SetDistributedMemoryCache(string key, Type type, object data)
=> _distributedCache.Set(key,
Encoding.UTF8.GetBytes(_serializer.Serialize(data, type)));

public TestDto GetDistributedMemoryCache(string key)
{
var cache = _distributedCache.Get(key);
return _serializer.Deserialize<TestDto>(Encoding.UTF8.GetString(cache));
}
}

Usage

var appService = host.Services.GetRequiredService<ITestApplicationService>();

appService.SetDistributedMemoryCache("test-key", typeof(TestDto), new TestDto("test data 1"));
var testData1 = appService.GetDistributedMemoryCache("test-key");

Console.WriteLine(testData1.Message); // test data 1

Example 3: Persistence Caching

Persistence caching wraps IGraphRepository<T> with a caching layer so that repeated queries return cached results instead of hitting the database.

Installation

NuGet Package
dotnet add package RCommon.Persistence.EFCore
NuGet Package
dotnet add package RCommon.MemoryCache

Configuration

Add AddInMemoryPersistenceCaching() to the EF Core persistence builder. This registers ICachingGraphRepository<T> implementations backed by IMemoryCache:

services.AddRCommon()
.WithPersistence<EFCorePerisistenceBuilder>(ef =>
{
ef.AddDbContext<TestDbContext>("TestDbContext", options =>
{
options.UseSqlServer(config.GetConnectionString("TestDbContext"));
});
ef.SetDefaultDataStore(dataStore =>
dataStore.DefaultDataStoreName = "TestDbContext");

// Registers ICachingGraphRepository<T>
ef.AddInMemoryPersistenceCaching();
})
.WithMemoryCaching<InMemoryCachingBuilder>(cache =>
{
cache.Configure(x =>
{
x.ExpirationScanFrequency = TimeSpan.FromMinutes(1);
});
});

Application Service

Instead of IGraphRepository<T>, inject ICachingGraphRepository<T>. The FindAsync overload accepts a cache key as the first argument:

public class TestApplicationService : ITestApplicationService
{
private readonly ICachingGraphRepository<Customer> _customerRepository;

public TestApplicationService(ICachingGraphRepository<Customer> customerRepository)
{
_customerRepository = customerRepository;
_customerRepository.DataStoreName = "TestDbContext";
}

public async Task<ICollection<Customer>> GetCustomers(object cacheKey)
{
// First call: hits the database and stores result under cacheKey
// Subsequent calls with the same cacheKey: returns cached result
return await _customerRepository.FindAsync(cacheKey, x => x.LastName == "Potter");
}
}

Usage

var appService = host.Services.GetRequiredService<ITestApplicationService>();

Console.WriteLine("Hitting the database w/ a query");
var customers = await appService.GetCustomers("my-test-key");
Console.WriteLine(customers);

Console.WriteLine("Hitting the cache");
customers = await appService.GetCustomers("my-test-key");
Console.WriteLine(customers);
// Second call returns from cache, no database roundtrip

Provider Comparison

ProviderPackageScopeSerialization RequiredBest For
InMemoryCachingBuilderRCommon.MemoryCacheSingle processNoFast in-process caching
DistributedMemoryCacheBuilderRCommon.MemoryCacheSingle processYesDevelopment, single-node
RedisCachingBuilderRCommon.RedisCacheMulti-process / distributedYesProduction distributed cache
AddInMemoryPersistenceCaching()RCommon.Persistence.EFCoreSingle processNoCaching repository query results

Key Design Points

Serialization for distributed caches. IDistributedCache stores byte[]. RCommon uses IJsonSerializer to serialize and deserialize objects. Register a serializer with WithJsonSerialization<> before calling WithDistributedCaching<>.

Cache keys for persistence caching. The cache key is a plain object. Use a meaningful, deterministic key that encodes the query parameters — for example, "customers:potter" rather than a GUID. The same key must be passed on subsequent calls to receive the cached result.

CacheDynamicallyCompiledExpressions() caches compiled LINQ expression trees, which avoids recompilation overhead for frequently evaluated predicates.

Swapping providers. The application service code depends only on IMemoryCache, IDistributedCache, IJsonSerializer, or ICachingGraphRepository<T>. Switching from in-memory to Redis requires a one-line change in Program.cs.

API Reference

TypePackagePurpose
InMemoryCachingBuilderRCommon.MemoryCacheRegisters IMemoryCache
DistributedMemoryCacheBuilderRCommon.MemoryCacheRegisters in-process IDistributedCache
RedisCachingBuilderRCommon.RedisCacheRegisters Redis-backed IDistributedCache
ICachingGraphRepository<T>RCommon.PersistenceCaching repository interface
IJsonSerializerRCommon.JsonSerialization abstraction
JsonNetBuilderRCommon.Json.JsonNetJson.NET serializer registration
RCommonRCommon