Sumapap.Persistence.Caching
1.2.1
dotnet add package Sumapap.Persistence.Caching --version 1.2.1
NuGet\Install-Package Sumapap.Persistence.Caching -Version 1.2.1
<PackageReference Include="Sumapap.Persistence.Caching" Version="1.2.1" />
<PackageVersion Include="Sumapap.Persistence.Caching" Version="1.2.1" />
<PackageReference Include="Sumapap.Persistence.Caching" />
paket add Sumapap.Persistence.Caching --version 1.2.1
#r "nuget: Sumapap.Persistence.Caching, 1.2.1"
#:package Sumapap.Persistence.Caching@1.2.1
#addin nuget:?package=Sumapap.Persistence.Caching&version=1.2.1
#tool nuget:?package=Sumapap.Persistence.Caching&version=1.2.1
Sumapap.Persistence.Caching
๐ก Overview
Sumapap.Persistence.Caching provides provider-agnostic caching infrastructure for Sumapap persistence repositories using the Visitor pattern. The package focuses on:
- Visitor-based cache decorator registration for extensibility
- Opt-in repository caching via
AllowCaching()configuration - Metadata-driven caching without tight coupling to specific cache implementations
- Centralized cache registry for inspection and provider consumption
- Granular control over which repository methods should be cached
The goal is to enable flexible, testable repository caching while maintaining clean separation between persistence and caching concerns.
โจ Why use Sumapap.Persistence.Caching?
- Visitor Pattern Architecture: Cache decoration logic is isolated in visitors, enabling extensibility without modifying core registration code
- Opt-in by Design: Repositories explicitly declare caching intent; caching is never forced globally
- Provider-Agnostic: Works with any cache implementation (FusionCache, Redis, MemoryCache) through registry consumption
- Metadata-Driven: Configuration is stored as metadata and applied later by cache providers, enabling inspection and testing
- Method-Level Control: Fine-grained configuration of which repository methods should be cached (reads only, all, or custom)
- Testable: Cache registry can be inspected in tests to verify caching configuration
๐ Quick start
- Add the package to your Infrastructure layer project:
dotnet add package Sumapap.Persistence.Caching
- Configure repositories with opt-in caching in your DI setup:
services.AddSumapap()
.WithRepositories(builder =>
{
builder
.AddScopedRepository<IUserRepository, UserRepository, User>()
.AllowCaching(config =>
{
config.Duration = TimeSpan.FromMinutes(5);
config.Methods.EnableAllReads(); // Cache all read operations
});
builder.UseRepositoryCaching(); // Register visitor
});
- Add a cache provider (e.g., FusionCache) to consume the registry:
services.AddSumapap()
.WithRepositories(builder =>
{
// ... repository registrations with AllowCaching()
builder.UseRepositoryCaching();
})
.UseFusionCache(); // Provider consumes RepositoryCacheRegistry
- The cache provider decorates registered repositories automatically based on metadata.
๐ Features and usage
Visitor Pattern Architecture
The caching infrastructure uses the Visitor pattern to separate cache decoration from core repository registration:
Repository Registration โ AllowCaching() โ Metadata Stored
โ
CachingRepositoryVisitor
โ
RepositoryCacheRegistry
โ
Cache Provider (FusionCache, etc.)
Key Components:
- CachingRepositoryVisitor: Inspects registrations and populates cache registry
- RepositoryCacheRegistry: Stores metadata about repositories with caching enabled
- RepositoryCacheConfiguration: Defines which methods to cache and cache behavior
- CachedFunctionsMapping: Default list of cacheable repository methods
Opt-In Caching Configuration
AllowCaching() - Enable caching for a repository with configuration:
builder
.AddScopedRepository<IProductRepository, ProductRepository, Product>()
.AllowCaching(config =>
{
config.Duration = TimeSpan.FromMinutes(10);
config.Methods.EnableAllReads(); // Cache all read operations
config.Metadata["Priority"] = "High"; // Custom metadata
});
Default Configuration (minimal):
builder
.AddScopedRepository<IOrderRepository, OrderRepository, Order>()
.AllowCaching(); // Uses default: 5 minutes, all read methods
Method-Level Cache Control
EnableAllReads() - Cache all read repository methods (default):
config.Methods.EnableAllReads();
// Caches: Find, GetAll, FirstOrDefault, SingleOrDefault, Count, Any, Stream*
EnableSpecific() - Cache only specific methods:
config.Methods.Clear();
config.Methods.EnableMethod("FindAsync");
config.Methods.EnableMethod("GetAllAsync");
// Only FindAsync and GetAllAsync are cached
DisableMethod() - Exclude specific methods from caching:
config.Methods.EnableAllReads();
config.Methods.DisableMethod("StreamAllAsync"); // Disable streaming method cache
Cache Registry Inspection
RepositoryCacheRegistry - Central registry of cached repository configurations:
public sealed class RepositoryCacheRegistry
{
public IReadOnlyList<RepositoryCacheEntry> CachedRepositories { get; }
}
Access the registry (useful for testing or runtime inspection):
var registry = serviceProvider.GetRequiredService<RepositoryCacheRegistry>();
foreach (var entry in registry.CachedRepositories)
{
Console.WriteLine($"Repository: {entry.RepositoryType.Name}");
Console.WriteLine($"Duration: {entry.Configuration.Duration}");
Console.WriteLine($"Cached Methods: {string.Join(", ", entry.Configuration.Methods)}");
}
Repository Cache Entry
RepositoryCacheEntry - Represents a cached repository registration:
public sealed class RepositoryCacheEntry
{
public required Type RepositoryType { get; init; }
public required Type EntityType { get; init; }
public required ServiceLifetime Lifetime { get; init; }
public required RepositoryCacheConfiguration Configuration { get; init; }
}
Caching Repository Visitor
CachingRepositoryVisitor - Processes registrations with caching metadata:
public class CachingRepositoryVisitor : IRepositoryRegistrationVisitor
{
public void Visit(RepositoryRegistrationEntry entry, IServiceCollection services)
{
if (!entry.AllowCaching || entry.CachingConfiguration is null)
return;
var cacheRegistry = GetOrCreateCacheRegistry(services);
cacheRegistry.Register(new RepositoryCacheEntry
{
RepositoryType = entry.ImplementationType,
EntityType = entry.EntityType,
Lifetime = entry.Lifetime,
Configuration = entry.CachingConfiguration
});
}
}
Default Cached Methods
By default, the following read-only repository methods are cached:
Synchronous:
Find(id)GetAll()FirstOrDefault()SingleOrDefault()Count()Any()
Asynchronous:
FindAsync(id)GetAllAsync()FirstOrDefaultAsync()SingleOrDefaultAsync()CountAsync()AnyAsync()StreamAllAsync()StreamWhereAsync()
Write operations are never cached (Add, Update, Delete, SaveChanges).
Cache Provider Integration
Cache providers consume the RepositoryCacheRegistry to apply decorators:
// Inside a cache provider (e.g., Sumapap.Persistence.FusionCache)
public static ISumapapBuilder UseFusionCache(this ISumapapBuilder builder)
{
builder.Services.AddFusionCache();
// Consume the cache registry
var registry = builder.Services.BuildServiceProvider()
.GetRequiredService<RepositoryCacheRegistry>();
foreach (var cacheEntry in registry.CachedRepositories)
{
// Decorate the repository service with caching logic
builder.Services.Decorate(cacheEntry.RepositoryType, (inner, sp) =>
{
var cache = sp.GetRequiredService<IFusionCache>();
return new CachedRepository(inner, cache, cacheEntry.Configuration);
});
}
return builder;
}
โ ๏ธ Notes & best practices
โ Do
- Always call
UseRepositoryCaching()after adding repositories withAllowCaching()to register the visitor - Use default configuration for typical CRUD repositories (covers common scenarios)
- Customize cache duration based on data volatility (short for frequently changing data, longer for reference data)
- Inspect the registry in tests to verify caching is configured correctly
- Use
DisableMethod()for expensive queries that should not be cached (e.g., large streaming operations)
โ Don''t
- Never cache write operations - the default configuration only caches reads; do not enable caching for
Add,Update,Delete - Avoid very long cache durations for frequently changing data (leads to stale reads)
- Don''t forget to register the visitor - calling
AllowCaching()withoutUseRepositoryCaching()has no effect - Avoid caching streaming methods for very large datasets (can cause memory pressure)
- Don''t use caching for real-time data where staleness is unacceptable
Cache Invalidation
This package only handles cache decoration and configuration. Cache invalidation is the responsibility of the cache provider (e.g., Sumapap.Persistence.FusionCache). Refer to your cache provider documentation for invalidation strategies.
Testing Recommendations
When testing repositories with caching:
- Test without cache first to verify repository logic
- Verify cache configuration by inspecting
RepositoryCacheRegistry - Test cache behavior by verifying cache provider decorators are applied
- Mock the cache provider when testing repository consumers to avoid cache side effects
โญ License
Distributed under the MIT License. See the LICENSE file in the repository for more information.
๐ฉ Contact
GitHub @muhammadirwanto-dev
Project Url https://github.com/muhammadirwanto-dev/sumapap
โ Support
If you like this project and want to support it, you can buy me a coffee๏ธ. Your coffee will keep me awake while developing this project โ.
<p align="center"> <a href="https://buymeacoffee.com/muhirwanto.dev"> <img src="https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20coffee&emoji=&slug=muhirwanto.dev&button_colour=FFDD00&font_colour=000000&font_family=Comic&outline_colour=000000&coffee_colour=ffffff" alt="Buy Me A Coffee"> </a> </p>
| 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
- Sumapap.Caching (>= 1.0.0)
- Sumapap.Persistence (>= 2.0.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Sumapap.Persistence.Caching:
| Package | Downloads |
|---|---|
|
Sumapap.Persistence.EfCore
`Sumapap.Persistence.EfCore` implements ReadWriteRepository and Unit of Work abstraction using `EntityFrameworkCore`. |
|
|
Sumapap.Persistence.Caching.FusionCache
This package provides an implementation of caching for the persistence layer of the Sumapap application using the FusionCache library. It offers a set of utilities and extensions that enable efficient caching of data retrieved from the persistence layer, improving performance and reducing latency in data access operations. The package is designed to be easily integrated with the existing persistence abstractions and can be used to cache results from repositories, queries, or any other data retrieval mechanisms within the application. |
GitHub repositories
This package is not used by any popular GitHub repositories.