codewithmukesh/dotnet-claude-kit

caching

Caching strategies for .NET 10 applications.

Ver código fuente

Cómo usar

  1. Copia el comando y revisa el paquete antes de instalarlo en tu entorno de agente.
  2. Consulta las herramientas, credenciales y entornos necesarios en el documento original.
  3. Describe las entradas y el resultado deseado; contrasta la salida con las fuentes.
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill caching

Antes de empezar

  • La instalación no incluye suscripción al modelo, cuota API ni permisos sobre tus archivos.
  • Las estrellas e instalaciones reflejan actividad, no una prueba independiente de calidad o seguridad.
Documento original del Skill

Contenido del repositorio de origen con títulos, ejemplos, código, tablas, enlaces e imágenes preservados.

Caching

Core Principles

  1. HybridCache is the default — .NET 9+ introduced HybridCache as the unified caching abstraction. It combines in-memory (L1) and distributed (L2) caching with stampede protection. See ADR-004.
  2. Cache reads, not writes — Cache GET operations. Invalidate on mutations. Never cache POST/PUT/DELETE responses.
  3. Output caching for entire responses — When the full HTTP response can be cached (public APIs, static data), use output caching middleware.
  4. Set explicit TTLs — Every cached item needs an expiration. No unbounded caches.

Patterns

HybridCache (Recommended Default)

csharp
// Program.cs
builder.Services.AddHybridCache(options =>
{
    options.DefaultEntryOptions = new HybridCacheEntryOptions
    {
        Expiration = TimeSpan.FromMinutes(5),
        LocalCacheExpiration = TimeSpan.FromMinutes(2)
    };
});

// Optional: Add Redis as the L2 distributed cache
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
});
csharp
// Usage in a handler
public class GetProduct
{
    public record Query(Guid Id);
    public record Response(Guid Id, string Name, decimal Price);

    internal class Handler(AppDbContext db, HybridCache cache)
    {
        public async Task<Response?> Handle(Query query, CancellationToken ct)
        {
            return await cache.GetOrCreateAsync(
                $"products:{query.Id}",
                async token => await db.Products
                    .Where(p => p.Id == query.Id)
                    .Select(p => new Response(p.Id, p.Name, p.Price))
                    .FirstOrDefaultAsync(token),
                new HybridCacheEntryOptions
                {
                    Expiration = TimeSpan.FromMinutes(10)
                },
                cancellationToken: ct);
        }
    }
}

Cache Invalidation

csharp
// Invalidate on mutation
public class UpdateProduct
{
    internal class Handler(AppDbContext db, HybridCache cache)
    {
        public async Task<Result> Handle(Command command, CancellationToken ct)
        {
            var product = await db.Products.FindAsync([command.Id], ct);
            if (product is null) return Result.Failure("Product not found");

            product.Update(command.Name, command.Price);
            await db.SaveChangesAsync(ct);

            // Invalidate the cached entry
            await cache.RemoveAsync($"products:{command.Id}", ct);

            return Result.Success();
        }
    }
}

Output Caching (Full Response Caching)

csharp
// Program.cs
builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(b => b.NoCache()); // Don't cache by default

    options.AddPolicy("ProductList", b => b
        .Expire(TimeSpan.FromMinutes(5))
        .Tag("products"));

    options.AddPolicy("ProductById", b => b
        .Expire(TimeSpan.FromMinutes(10))
        .SetVaryByRouteValue("id")
        .Tag("products"));
});

app.UseOutputCache();

// Apply to endpoints
group.MapGet("/", ListProducts).CacheOutput("ProductList");
group.MapGet("/{id:guid}", GetProduct).CacheOutput("ProductById");

// Invalidate by tag on mutations
group.MapPut("/{id:guid}", async (Guid id, UpdateProductRequest request,
    IOutputCacheStore store, CancellationToken ct) =>
{
    // ... update logic ...
    await store.EvictByTagAsync("products", ct);
    return TypedResults.NoContent();
});

Cache-Aside Pattern (Legacy)

Prefer HybridCache for all new code. Manual IDistributedCache cache-aside lacks stampede protection, requires manual serialization, and has no L1/L2 layering. Use only when integrating with existing code that already uses IDistributedCache directly.

Anti-patterns

Don't Cache Without Expiration

csharp
// BAD — cache lives forever, stale data guaranteed
await cache.SetStringAsync(key, value);

// GOOD — always set TTL
await cache.SetStringAsync(key, value, new DistributedCacheEntryOptions
{
    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
});

Don't Cache Mutable User-Specific Data

csharp
// BAD — caching user's cart with a global key
await cache.GetOrCreateAsync("shopping-cart", ...);

// GOOD — include user ID in key
await cache.GetOrCreateAsync($"shopping-cart:{userId}", ...);

Don't Build Your Own Stampede Protection

csharp
// BAD — manual lock to prevent cache stampede
private static readonly SemaphoreSlim Lock = new(1, 1);
await Lock.WaitAsync();
try { /* check cache, populate if missing */ }
finally { Lock.Release(); }

// GOOD — HybridCache has built-in stampede protection
await hybridCache.GetOrCreateAsync(key, factory);

Decision Guide

ScenarioRecommendation
General data cachingHybridCache (GetOrCreateAsync)
Full HTTP responseOutput caching with .CacheOutput()
Frequently read, rarely writtenHybridCache with longer TTL
User-specific dataHybridCache with user-scoped key
Cache invalidation on writecache.RemoveAsync() or output cache tags
Distributed deploymentHybridCache + Redis L2 backend
Single-server deploymentHybridCache with in-memory only

Relacionados por tarea

del mismo repositorio

Más Skills

Todos los Skills
codewithmukesh
Comunidad

clean-architecture

Clean Architecture for .NET applications. Covers the 4-project layout (Domain, Application, Infrastructure, Api), dependency inversion, use case handlers, domain entities with behavior, and infrastructure as a plugin. Load this skill when building a project with Clean Architecture, discussing layered architecture, dependency inversion, use cases, or when the architecture-advisor recommends Clean Architecture.

Instalaciones / 1h
3
GitHub Stars
797
Actualizado
7 ago
codewithmukesh
Comunidad

arch-check

Architecture conformance check: verifies an existing codebase against its declared architecture (VSA, Clean Architecture, DDD, Modular Monolith) — dependency direction, layer violations, module boundary leaks, and cycles — using token-cheap Roslyn MCP analysis. Invoke when: "check architecture", "architecture violations", "layer violations", "dependency direction", "module boundaries", "arch check", "is my architecture clean", "enforce architecture", "conformance check". For CHOOSING an architec

Instalaciones / 1h
2
GitHub Stars
789
Actualizado
7 ago
codewithmukesh
Comunidad

aspire

.NET Aspire for cloud-native orchestration. Covers AppHost configuration, service defaults, resource configuration, service discovery, and the Aspire dashboard. Load this skill when setting up local development orchestration, service discovery, or Aspire-managed infrastructure, or when the user mentions "Aspire", "AppHost", "service defaults", "service discovery", "orchestration", "Aspire dashboard", "AddProject", "WithReference", or "cloud-native .NET".

Instalaciones / 1h
2
GitHub Stars
789
Actualizado
7 ago
codewithmukesh
Comunidad

build-fix

Autonomous iteration loops for .NET: drive a broken build or failing test suite to green with bounded iterations, progress detection, and fail-safe guards that prevent infinite retries and wasted tokens. The build-fix loop (dotnet build, parse, categorize, fix, rebuild) is the primary flow; the test-fix loop is a first-class variant. Invoke when the build is broken, after a major refactor or dependency update, or when the user says "fix the build", "build is broken", "make it compile", "make the

Instalaciones / 1h
2
GitHub Stars
789
Actualizado
7 ago