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
                    
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="Sumapap.Persistence.Caching" Version="1.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sumapap.Persistence.Caching" Version="1.2.1" />
                    
Directory.Packages.props
<PackageReference Include="Sumapap.Persistence.Caching" />
                    
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 Sumapap.Persistence.Caching --version 1.2.1
                    
#r "nuget: Sumapap.Persistence.Caching, 1.2.1"
                    
#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 Sumapap.Persistence.Caching@1.2.1
                    
#: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=Sumapap.Persistence.Caching&version=1.2.1
                    
Install as a Cake Addin
#tool nuget:?package=Sumapap.Persistence.Caching&version=1.2.1
                    
Install as a Cake Tool

Sumapap.Persistence.Caching

NuGet Version NuGet Downloads License GitHub Issues GitHub Stars GitHub Forks Contributions Welcome

๐Ÿ’ก 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

  1. Add the package to your Infrastructure layer project:
dotnet add package Sumapap.Persistence.Caching
  1. 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
    });
  1. 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
  1. 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:

  1. CachingRepositoryVisitor: Inspects registrations and populates cache registry
  2. RepositoryCacheRegistry: Stores metadata about repositories with caching enabled
  3. RepositoryCacheConfiguration: Defines which methods to cache and cache behavior
  4. 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 with AllowCaching() 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() without UseRepositoryCaching() 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:

  1. Test without cache first to verify repository logic
  2. Verify cache configuration by inspecting RepositoryCacheRegistry
  3. Test cache behavior by verifying cache provider decorators are applied
  4. 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 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. 
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 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.

Version Downloads Last Updated
1.2.1 105 5/29/2026
1.2.0 94 5/29/2026
1.1.0 136 5/25/2026
1.0.0 139 5/24/2026