using Microsoft.Extensions.Caching.Distributed; using System.Text.Json;
namespace MiApp.Cache { public interface IAlmacenCache { Task<T?> RecuperarAsync<T>(string clave); Task AlmacenarAsync<T>(string clave, T datos, TimeSpan? duracion = null); Task BorrarAsync(string clave); Task VerificarExistenciaAsync(string clave); Task EliminarPorPatronAsync(string patron); } }
</div></div>Se implementa la inetrfaz con una clase que maneja la serialización y el control de claves. La lógica de seguimiento de claves se basa en una clave especial dentro del mismo caché distribuido. <div><div>```
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;
namespace MiApp.Cache
{
public class AlmacenCacheDistribuido : IAlmacenCache
{
private readonly IDistributedCache _almacen;
private const string ClaveRaiz = "__todas_las_claves__";
public AlmacenCacheDistribuido(IDistributedCache almacen)
{
_almacen = almacen;
}
public async Task<T?> RecuperarAsync<T>(string clave)
{
var bytes = await _almacen.GetAsync(clave);
if (bytes == null) return default;
return JsonSerializer.Deserialize<T>(bytes);
}
public async Task AlmacenarAsync<T>(string clave, T datos, TimeSpan? duracion = null)
{
var opciones = new DistributedCacheEntryOptions();
if (duracion.HasValue)
opciones.AbsoluteExpirationRelativeToNow = duracion;
else
opciones.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2);
var bytes = JsonSerializer.SerializeToUtf8Bytes(datos);
await _almacen.SetAsync(clave, bytes, opciones);
await RegistrarClaveAsync(clave);
}
public async Task BorrarAsync(string clave)
{
await _almacen.RemoveAsync(clave);
await QuitarClaveAsync(clave);
}
public async Task<bool> VerificarExistenciaAsync(string clave)
{
var datos = await _almacen.GetAsync(clave);
return datos != null;
}
public async Task EliminarPorPatronAsync(string patron)
{
var claves = await ObtenerTodasClavesAsync();
if (claves == null) return;
var clavesAEliminar = claves.Where(c => c.Contains(patron)).ToList();
foreach (var clave in clavesAEliminar)
await _almacen.RemoveAsync(clave);
var clavesActualizadas = claves.Except(clavesAEliminar).ToList();
await GuardarListaClavesAsync(clavesActualizadas);
}
private async Task RegistrarClaveAsync(string clave)
{
var listaActual = await ObtenerTodasClavesAsync() ?? new List<string>();
if (!listaActual.Contains(clave))
{
listaActual.Add(clave);
await GuardarListaClavesAsync(listaActual);
}
}
private async Task QuitarClaveAsync(string clave)
{
var listaActual = await ObtenerTodasClavesAsync();
if (listaActual != null && listaActual.Remove(clave))
{
await GuardarListaClavesAsync(listaActual);
}
}
private async Task<List<string>?> ObtenerTodasClavesAsync()
{
var datos = await _almacen.GetStringAsync(ClaveRaiz);
return datos == null ? null : JsonSerializer.Deserialize<List<string>>(datos);
}
private async Task GuardarListaClavesAsync(List<string> claves)
{
await _almacen.SetStringAsync(ClaveRaiz, JsonSerializer.Serialize(claves));
}
}
}
using MiApp.Cache; using Microsoft.Extensions.DependencyInjection;
namespace MiApp.Configuracion { public static class ConfiguracionCache { public static void AgregarServicioCache(this IServiceCollection servicios) { servicios.AddDistributedMemoryCache(); servicios.AddSingleton<IAlmacenCache, AlmacenCacheDistribuido>(); } } }
</div></div>En el archivo principle de la aplicación, se invoca el método de extensión para habilitar el caché. <div>```
builder.Services.AgregarServicioCache();
public class ModuloAutofac : Autofac.Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType<AlmacenCacheDistribuido>() .As<IAlmacenCache>() .SingleInstance(); } }
</div>Un ejemplo de uso en un controlador de API demuestra cómo inyectar y utilizar el servicio de caché. <div><div>```
using Microsoft.AspNetCore.Mvc;
using MiApp.Cache;
namespace MiApp.Controladores
{
[ApiController]
[Route("api/[controller]")]
public class DatosController : ControllerBase
{
private readonly IAlmacenCache _cache;
public DatosController(IAlmacenCache cache)
{
_cache = cache;
}
[HttpGet("{id}")]
public async Task<IActionResult> ObtenerDato(string id)
{
var claveCache = $"dato:{id}";
var datoCacheado = await _cache.RecuperarAsync<DatoEjemplo>(claveCache);
if (datoCacheado != null)
return Ok(datoCacheado);
var datoOriginal = await ConsultarDatoDeFuenteAsync(id);
await _cache.AlmacenarAsync(claveCache, datoOriginal, TimeSpan.FromMinutes(10));
return Ok(datoOriginal);
}
private Task<DatoEjemplo> ConsultarDatoDeFuenteAsync(string id)
{
// Simulación de consulta a base de datos u otro origen
var dato = new DatoEjemplo
{
Identificador = id,
Valor = "Valor generado",
MarcaTiempo = DateTime.UtcNow
};
return Task.FromResult(dato);
}
}
public class DatoEjemplo
{
public string Identificador { get; set; }
public string Valor { get; set; }
public DateTime MarcaTiempo { get; set; }
}
}