Implementación de caché distribuido en .NET 8 mediante abstracciones

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; }
    }
}

Etiquetas: dotnet8 caching idistributedcache dependency-injection CSharp

Publicado el 6-15 23:15