Saltar al contenido principal

Tienda de Caché Personalizada

Reemplaza el store en memoria con un backend de caché distribuido implementando ICacheStore.

Interfaz ICacheStore

public interface ICacheStore
{
Task<T?> GetAsync<T>(string key, CancellationToken ct = default);
Task SetAsync<T>(string key, T value, TimeSpan? absoluteExpiration, TimeSpan? slidingExpiration, CancellationToken ct = default);
Task RemoveAsync(string key, CancellationToken ct = default);
}

// Para invalidación basada en grupos
public interface IGroupAwareCacheStore : ICacheStore
{
Task RemoveGroupAsync(string group, CancellationToken ct = default);
Task AddToGroupAsync(string key, string group, CancellationToken ct = default);
}

Implementación Redis

public class RedisCacheStore : IGroupAwareCacheStore
{
private readonly IDatabase _db;

public RedisCacheStore(IConnectionMultiplexer redis)
{
_db = redis.GetDatabase();
}

public async Task<T?> GetAsync<T>(string key, CancellationToken ct = default)
{
var value = await _db.StringGetAsync(key);
if (!value.HasValue) return default;
return JsonSerializer.Deserialize<T>(value!);
}

public async Task SetAsync<T>(
string key,
T value,
TimeSpan? absoluteExpiration,
TimeSpan? slidingExpiration,
CancellationToken ct = default)
{
var serialized = JsonSerializer.Serialize(value);
var expiry = absoluteExpiration ?? slidingExpiration;
await _db.StringSetAsync(key, serialized, expiry);
}

public async Task RemoveAsync(string key, CancellationToken ct = default)
=> await _db.KeyDeleteAsync(key);

public async Task AddToGroupAsync(string key, string group, CancellationToken ct = default)
=> await _db.SetAddAsync($"group:{group}", key);

public async Task RemoveGroupAsync(string group, CancellationToken ct = default)
{
var groupKey = $"group:{group}";
var members = await _db.SetMembersAsync(groupKey);
var keys = members.Select(m => (RedisKey)(string)m!).Append(groupKey).ToArray();
await _db.KeyDeleteAsync(keys);
}
}

Registro

builder.Services.AddSingleton<IConnectionMultiplexer>(
ConnectionMultiplexer.Connect(builder.Configuration["Redis:ConnectionString"]!));

builder.Services.AddSingleton<ICacheStore, RedisCacheStore>();
builder.Services.AddSingleton<IGroupAwareCacheStore, RedisCacheStore>();
tip

Para cargas de trabajo en producción, usa caché distribuida (Redis, SQL Server) para que múltiples instancias de tu aplicación compartan la misma caché y las invalidaciones se propaguen correctamente.