Rystem.RepositoryFramework.Cache
10.0.7
dotnet add package Rystem.RepositoryFramework.Cache --version 10.0.7
NuGet\Install-Package Rystem.RepositoryFramework.Cache -Version 10.0.7
<PackageReference Include="Rystem.RepositoryFramework.Cache" Version="10.0.7" />
<PackageVersion Include="Rystem.RepositoryFramework.Cache" Version="10.0.7" />
<PackageReference Include="Rystem.RepositoryFramework.Cache" />
paket add Rystem.RepositoryFramework.Cache --version 10.0.7
#r "nuget: Rystem.RepositoryFramework.Cache, 10.0.7"
#:package Rystem.RepositoryFramework.Cache@10.0.7
#addin nuget:?package=Rystem.RepositoryFramework.Cache&version=10.0.7
#tool nuget:?package=Rystem.RepositoryFramework.Cache&version=10.0.7
Rystem.RepositoryFramework.Cache
Rystem.RepositoryFramework.Cache adds cache decorators on top of Repository Framework registrations. You keep resolving the same IRepository<T, TKey>, IQuery<T, TKey>, or ICommand<T, TKey> services, but the runtime pipeline first checks a cache layer and then falls back to the original storage implementation.
The package is intentionally thin: it does not replace your storage, it decorates it.
Installation
dotnet add package Rystem.RepositoryFramework.Cache
How the cache layer is wired
The package registers one of these decorators:
CachedRepository<T, TKey>for full repositories.CachedQuery<T, TKey>for query-only registrations.CachedRepository<T, TKey>for command-only registrations, but only when cache options include write methods.
That means caching is opt-in per registration and per method group.
builder.Services.AddRepository<Country, CountryKey>(repositoryBuilder =>
{
repositoryBuilder.WithInMemory(builder =>
{
builder.PopulateWithRandomData(100, 100);
});
repositoryBuilder.WithInMemoryCache(cache =>
{
cache.ExpiringTime = TimeSpan.FromSeconds(10);
cache.Methods = RepositoryMethods.Get
| RepositoryMethods.Query
| RepositoryMethods.Exist;
});
});
After registration, the resolved IRepository<Country, CountryKey> behaves like this:
- try cache
- fall back to the underlying repository when needed
- write the result back to cache when the configured method allows it
Supported cache backends
WithInMemoryCache(...)
Uses the package-provided InMemoryCache<T, TKey> over IMemoryCache.
- Calls
AddMemoryCache()automatically. - Registers the cache service as
Singleton. - Good fit for single-instance apps or as the first level of a multi-layer cache.
builder.Services.AddRepository<Plant, int>(repositoryBuilder =>
{
repositoryBuilder.WithInMemory();
repositoryBuilder.WithInMemoryCache(cache =>
{
cache.ExpiringTime = TimeSpan.FromMinutes(1);
cache.Methods = RepositoryMethods.Query | RepositoryMethods.Get;
});
});
WithDistributedCache(...)
Wraps the Microsoft.Extensions.Caching.Distributed.IDistributedCache that is already in DI.
- The Repository Framework adapter serializes values as JSON.
- The default adapter sets
AbsoluteExpiration,AbsoluteExpirationRelativeToNow, andSlidingExpirationto the sameExpiringTime. - Registers the adapter as
Singletonby default.
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "RepositoryFramework";
});
builder.Services.AddRepository<Country, CountryKey>(repositoryBuilder =>
{
repositoryBuilder.WithInMemory(builder =>
{
builder.PopulateWithRandomData(100, 100);
});
repositoryBuilder.WithDistributedCache(cache =>
{
cache.ExpiringTime = TimeSpan.FromSeconds(10);
cache.Methods = RepositoryMethods.Query | RepositoryMethods.Get | RepositoryMethods.Exist;
});
});
WithCache<T, TKey, TCache>(...)
Use this when you want a custom in-process or custom remote cache implementation.
public sealed class MyCache<T, TKey> : ICache<T, TKey>
where TKey : notnull
{
public Task<CacheResponse<TValue>> RetrieveAsync<TValue>(
string key,
CancellationToken cancellationToken = default)
=> Task.FromResult(new CacheResponse<TValue>(false, default));
public Task<bool> SetAsync<TValue>(
string key,
TValue value,
CacheOptions<T, TKey> options,
CancellationToken? cancellationToken = null)
=> Task.FromResult(true);
public Task<bool> DeleteAsync(
string key,
CancellationToken cancellationToken = default)
=> Task.FromResult(true);
}
builder.Services.AddRepository<Plant, int>(repositoryBuilder =>
{
repositoryBuilder.WithInMemory();
repositoryBuilder.WithCache<Plant, int, MyCache<Plant, int>>(cache =>
{
cache.ExpiringTime = TimeSpan.FromMinutes(5);
cache.Methods = RepositoryMethods.All;
});
});
WithDistributedCache<T, TKey, TCache>(...)
Use this when your custom cache should be treated as a distributed cache layer.
TCache must implement IDistributedCache<T, TKey>, which extends ICache<T, TKey>.
CQRS registrations
The cache package works on full repositories and on CQRS-only registrations.
Query only
builder.Services.AddQuery<Plant, int>(queryBuilder =>
{
queryBuilder.WithInMemory();
queryBuilder.WithInMemoryCache(cache =>
{
cache.ExpiringTime = TimeSpan.FromMinutes(5);
});
});
Command only
For command-only registrations, the decorator is added only when you cache write methods.
builder.Services.AddCommand<Plant, int>(commandBuilder =>
{
commandBuilder.WithInMemory();
commandBuilder.WithInMemoryCache(cache =>
{
cache.ExpiringTime = TimeSpan.FromMinutes(1);
cache.Methods = RepositoryMethods.Insert
| RepositoryMethods.Update
| RepositoryMethods.Delete;
});
});
If you configure only Get, Query, or Exist on a command registration, there is no command decorator to apply those settings.
Two-level caching
The decorators support having both local and distributed cache layers on the same registration. The unit tests wire exactly that pattern with in-memory storage plus in-memory cache plus Redis-backed distributed cache.
builder.Services.AddRepository<Country, CountryKey>(repositoryBuilder =>
{
repositoryBuilder.WithInMemory(builder =>
{
builder.PopulateWithRandomData(100, 100);
});
repositoryBuilder
.WithInMemoryCache(cache =>
{
cache.ExpiringTime = TimeSpan.FromSeconds(10);
})
.WithDistributedCache(cache =>
{
cache.ExpiringTime = TimeSpan.FromSeconds(10);
});
});
Read flow in that setup:
- check
ICache<T, TKey> - check
IDistributedCache<T, TKey> - hit the underlying repository
- backfill any cache layers that missed
What is cached
Read methods
GetAsync(key)caches the returned model.ExistAsync(key)caches the returnedState<T, TKey>.QueryAsync(filter)materializes the full async stream to aList<Entity<T, TKey>>and caches that list.OperationAsync(...)is also cached, and it uses theQueryflag to decide whether caching is enabled.
Because query results are materialized before caching, this package is best for repeated reads of the same filter, not for preserving streaming behavior end-to-end.
Write methods
InsertAsynccan refreshGetandExistcache entries for the affected key.UpdateAsynccan refreshGetandExistcache entries for the affected key.DeleteAsynccan removeGetandExistcache entries for the affected key.BatchAsyncupdates or removes per-keyGetandExistentries as it iterates through batch results.
The package does not keep a registry of query filters, so write operations do not invalidate previously cached query results.
That behavior is visible in RepositoryFramework.UnitTest/Tests/Cache/CacheTest.cs: deleting all records from the underlying repository does not remove the cached query result, so the old list is served again until cache expiration.
Cache key behavior
The decorators build their own cache keys.
GetandExistinclude method name, model type name, factory name, and serialized key.Queryincludes method name, model type name, and the filter key.Operationincludes method name, operation name, model type name, and the filter key.
Practical consequence:
- named repository registrations are respected for
GetandExist - query and operation caches are not partitioned by factory name in the current implementation
If you use multiple named registrations for the same model with different backends, document that behavior for your team before enabling query caching.
Defaults and options
CacheOptions<T, TKey>
| Property | Default | Notes |
|---|---|---|
Methods |
Query | Get | Exist |
Controls which operations participate in caching. |
ExpiringTime |
365 days |
Used by the built-in in-memory cache and passed to custom caches. |
HasCommandPattern becomes true when Methods includes Insert, Update, Delete, or All.
DistributedCacheOptions<T, TKey>
Inherits from CacheOptions<T, TKey>.
| Property | Default | Notes |
|---|---|---|
Methods |
Query | Get | Exist |
Same behavior as CacheOptions<T, TKey>. |
ExpiringTime |
365 * 365 days |
Very large default; set it explicitly for real workloads. |
RepositoryMethods flags in cache scenarios
| Flag | Effect |
|---|---|
Get |
Enables read-through caching for GetAsync. |
Exist |
Enables read-through caching for ExistAsync. |
Query |
Enables caching for QueryAsync and OperationAsync. |
Insert |
After insert, refreshes key-based cache entries. |
Update |
After update, refreshes key-based cache entries. |
Delete |
After delete, removes key-based cache entries. |
All |
Enables every flag. |
Warm-up and lifecycle notes
The cache package does not add its own bootstrap workflow. Any warm-up still comes from the underlying repository registration, for example when an in-memory repository is populated through PopulateWithRandomData(...) and then activated via WarmUpAsync().
When to use this package
Use it when you want to:
- add read caching without changing your repository consumers
- stack in-memory and distributed cache decorators
- keep cache concerns outside your domain repositories
- experiment with caching policies per registration or per named factory
If you also need a Blob Storage-backed distributed cache provider, see ../RepositoryFramework.Cache.Azure.Storage.Blob/README.md.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Caching.Memory (>= 10.0.5)
- Rystem.RepositoryFramework.Abstractions (>= 10.0.7)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Rystem.RepositoryFramework.Cache:
| Package | Downloads |
|---|---|
|
Rystem.RepositoryFramework.Cache.Azure.Storage.Blob
Rystem.RepositoryFramework allows you to use correctly concepts like repository pattern, CQRS and DDD. You have interfaces for your domains, auto-generated api, auto-generated HttpClient to simplify connection "api to front-end", a functionality for auto-population in memory of your models, a functionality to simulate exceptions and waiting time from external sources to improve your implementation/business test and load test. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.7 | 132 | 3/26/2026 |
| 10.0.6 | 307,241 | 3/3/2026 |
| 10.0.5 | 191 | 2/22/2026 |
| 10.0.4 | 195 | 2/9/2026 |
| 10.0.3 | 147,970 | 1/28/2026 |
| 10.0.1 | 209,395 | 11/12/2025 |
| 9.1.3 | 324 | 9/2/2025 |
| 9.1.2 | 764,853 | 5/29/2025 |
| 9.1.1 | 97,938 | 5/2/2025 |
| 9.0.32 | 186,759 | 4/15/2025 |
| 9.0.31 | 5,826 | 4/2/2025 |
| 9.0.30 | 88,830 | 3/26/2025 |
| 9.0.29 | 9,043 | 3/18/2025 |
| 9.0.28 | 293 | 3/17/2025 |
| 9.0.27 | 281 | 3/16/2025 |
| 9.0.26 | 301 | 3/13/2025 |
| 9.0.25 | 52,159 | 3/9/2025 |
| 9.0.21 | 773 | 3/6/2025 |
| 9.0.20 | 19,618 | 3/6/2025 |
| 9.0.19 | 356 | 3/6/2025 |