Nosabit.Adapters.Persistence.ICacheHybrid 1.2.1

dotnet add package Nosabit.Adapters.Persistence.ICacheHybrid --version 1.2.1                
NuGet\Install-Package Nosabit.Adapters.Persistence.ICacheHybrid -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="Nosabit.Adapters.Persistence.ICacheHybrid" Version="1.2.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Nosabit.Adapters.Persistence.ICacheHybrid --version 1.2.1                
#r "nuget: Nosabit.Adapters.Persistence.ICacheHybrid, 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.
// Install Nosabit.Adapters.Persistence.ICacheHybrid as a Cake Addin
#addin nuget:?package=Nosabit.Adapters.Persistence.ICacheHybrid&version=1.2.1

// Install Nosabit.Adapters.Persistence.ICacheHybrid as a Cake Tool
#tool nuget:?package=Nosabit.Adapters.Persistence.ICacheHybrid&version=1.2.1                

Nosabit.Adapters.Persistence.ICacheHybrid

Implementación eficiente de caché híbrido para aplicaciones .NET que integra Microsoft.Extensions.Caching.Hybrid, proporcionando un sistema de almacenamiento en caché combinado (memoria y distribuido) con control granular de la invalidación y soporte para claves compuestas.

NuGet License: MIT

Instalación

Package Manager

Install-Package Nosabit.Adapters.Persistence.ICacheHybrid

.NET CLI

dotnet add package Nosabit.Adapters.Persistence.ICacheHybrid

PackageReference

<PackageReference Include="Nosabit.Adapters.Persistence.ICacheHybrid" Version="1.1.0" />

Características principales

  • Implementación completa de ICacheHybrid: Proporciona la implementación de las interfaces definidas en Nosabit.Adapters.Persistence.ICacheHybrid.Abstractions
  • Basado en Microsoft.Extensions.Caching.Hybrid: Utiliza el potente sistema de caché híbrido de Microsoft
  • Gestión inteligente de claves: Mantiene un registro de todas las claves de caché para facilitar la invalidación
  • Agrupación automática por contexto: Organiza automáticamente las claves de caché por tipo de servicio
  • Control de expiración flexible: Permite personalizar la duración de cada entrada de caché
  • Invalidación selectiva: Proporciona métodos para invalidar grupos específicos de caché
  • Integración con ASP.NET Core: Fácil configuración mediante métodos de extensión
  • Alto rendimiento: Optimizado para operaciones de caché frecuentes

Componentes principales

NvCacheHybrid

Implementación principal que interactúa directamente con el HybridCache de Microsoft:

public class NvCacheHybrid : INvCacheHybrid
{
    // Almacena un registro de todas las claves de caché para facilitar la invalidación
    private ConcurrentDictionary<string, ConcurrentBag<string>> KeysCache = 
        new ConcurrentDictionary<string, ConcurrentBag<string>>();
    
    // Construye claves únicas y las registra para su seguimiento
    private string GetKeys(string mainKey, dynamic?[] keys);
    
    // Implementación de los métodos de obtención y gestión de caché
    public ValueTask<T> GetOrCreateAsync<T>(string GroupKey, Func<Task<T>> factory, dynamic?[] deps);
    public ValueTask<T> GetOrCreateAsync<T>(string GroupKey, Func<Task<T>> factory, dynamic?[] deps, TimeSpan duration);
    public Task Remove(string key);
    public Task Remove(string[] keys);
    public Task RemoveAll();
}

NvCacheHybridAdapter

Adaptador genérico que implementa la interfaz ICacheHybrid<T>:

public class NvCacheHybridAdapter<TClass> : ICacheHybrid<TClass>
{
    // El nombre del tipo se usa como clave principal para agrupar entradas de caché
    private readonly string MainKey = typeof(TClass).Name;
    
    // Implementación de los métodos definidos en la interfaz
    public ValueTask<T> GetOrCreateAsync<T>(Func<Task<T>> factory, dynamic?[] deps);
    public ValueTask<T> GetOrCreateAsync<T>(Func<Task<T>> factory, dynamic?[] deps, TimeSpan duration);
    public Task RemoveCache();
    public Task RemoveCache(Enum ands);
    public Task RemoveCache(params Enum[] ands);
    public Task RemoveCacheAll();
}

ICacheHybridContainerDI

Extensiones para la configuración del servicio:

public static class ICacheHybridContainerDI
{
    // Configura los servicios necesarios para el sistema de caché híbrido
    public static WebApplicationBuilder AddICacheHybridContainerDI(
        this WebApplicationBuilder builder, 
        int Expiration = 5);
}

Configuración

Agrega el sistema de caché híbrido a tu aplicación ASP.NET Core:

// En Program.cs
var builder = WebApplication.CreateBuilder(args);

// Añadir el servicio de caché híbrido con la expiración predeterminada (5 minutos)
builder.AddICacheHybridContainerDI();

// O especificar un tiempo de expiración personalizado (en minutos)
builder.AddICacheHybridContainerDI(Expiration: 10);

var app = builder.Build();
// Resto de la configuración...

Ejemplos de uso

Servicio básico con caché

public class ProductoService
{
    private readonly ISqlDataAccess _db;
    private readonly ICacheHybrid<ProductoService> _cache;
    
    public ProductoService(ISqlDataAccess db, ICacheHybrid<ProductoService> cache)
    {
        _db = db;
        _cache = cache;
    }
    
    // Método que utiliza el caché para datos raramente cambiantes
    public async Task<IEnumerable<Producto>> ObtenerTodosAsync()
    {
        return await _cache.GetOrCreateAsync(
            () => _db.QueryAsync<Producto>("sp_ObtenerProductos"),
            [nameof(ObtenerTodosAsync)]
        );
    }
    
    // Método con clave compuesta para caché de elementos individuales
    public async Task<Producto> ObtenerPorIdAsync(int id)
    {
        return await _cache.GetOrCreateAsync(
            () => _db.QueryFirstAsync<Producto>("sp_ObtenerProductoPorId", new { Id = id }),
            [nameof(ObtenerPorIdAsync), id]
        );
    }
    
    // Método con duración de caché personalizada para datos volátiles
    public async Task<IEnumerable<Producto>> ObtenerDestacadosAsync()
    {
        return await _cache.GetOrCreateAsync(
            () => _db.QueryAsync<Producto>("sp_ObtenerProductosDestacados"),
            [nameof(ObtenerDestacadosAsync)],
            TimeSpan.FromMinutes(2) // Caché de corta duración
        );
    }
}

Invalidación granular con enumeraciones

public class VentaService
{
    private readonly ISqlDataAccess _db;
    private readonly ICacheHybrid<ProductoService> _productoCache;
    
    // Enum para categorizar diferentes tipos de caché
    public enum ProductoCacheTipo
    {
        Listado,
        Detalle,
        Destacados,
        Stock
    }
    
    public VentaService(
        ISqlDataAccess db, 
        ICacheHybrid<ProductoService> productoCache)
    {
        _db = db;
        _productoCache = productoCache;
    }
    
    // Método que invalida tipos específicos de caché
    public async Task<DBEntity> RegistrarVentaAsync(Venta venta)
    {
        var result = await _db.ExecuteAsync("sp_RegistrarVenta", venta);
        
        // Invalidar solo las cachés de stock y destacados
        await _productoCache.RemoveCache(
            ProductoCacheTipo.Stock, 
            ProductoCacheTipo.Destacados
        );
        
        return result;
    }
    
    // Método que invalida toda la caché de un servicio
    public async Task<DBEntity> ActualizarCatalogoAsync()
    {
        var result = await _db.ExecuteAsync("sp_ActualizarCatalogo");
        
        // Invalidar todas las cachés relacionadas con productos
        await _productoCache.RemoveCacheAll();
        
        return result;
    }
}

Patrones de uso en controladores

[ApiController]
[Route("api/[controller]")]
public class ProductosController : ControllerBase
{
    private readonly IProductoService _productoService;
    
    public ProductosController(IProductoService productoService)
    {
        _productoService = productoService;
    }
    
    [HttpGet]
    public async Task<IActionResult> GetAll()
    {
        // El servicio ya gestiona la caché internamente
        var productos = await _productoService.ObtenerTodosAsync();
        return Ok(productos);
    }
    
    [HttpGet("{id}")]
    public async Task<IActionResult> GetById(int id)
    {
        var producto = await _productoService.ObtenerPorIdAsync(id);
        if (producto == null)
            return NotFound();
            
        return Ok(producto);
    }
    
    [HttpPost]
    public async Task<IActionResult> Create(ProductoDto dto)
    {
        // Al crear un producto, el servicio invalidará las cachés apropiadas
        var result = await _productoService.GuardarAsync(dto);
        
        if (result.CodeError != 0)
            return BadRequest(result);
            
        return CreatedAtAction(nameof(GetById), new { id = dto.Id }, dto);
    }
}

Cómo funciona internamente

  1. Registro de claves: Cada vez que se crea una entrada en caché, la clave se registra en un diccionario para facilitar la invalidación posterior.

  2. Agrupación por tipo: Las claves se agrupan automáticamente según el tipo genérico del servicio (TClass), lo que permite una invalidación selectiva.

  3. Composición de claves: Las claves de caché se componen combinando el nombre del tipo, el nombre del método y los parámetros adicionales.

  4. Invalidación jerárquica: Puedes invalidar:

    • Una entrada específica
    • Un grupo de entradas relacionadas con un enum
    • Todas las entradas de un servicio específico
    • Todas las entradas de caché de la aplicación

Mejores prácticas

  1. Usar tipos específicos para cada servicio: Crea una instancia de ICacheHybrid<T> para cada servicio.

  2. Definir enumeraciones para tipos de caché: Utiliza enums para categorizar diferentes tipos de caché dentro de un servicio.

  3. Establecer duraciones apropiadas: Utiliza duraciones más cortas para datos que cambian con frecuencia.

  4. Ser consistente con los nombres de claves: Utiliza nameof(método) para garantizar consistencia en las claves.

  5. Invalidar con precisión: Invalida solo las entradas de caché afectadas por una operación.

  6. Considerar el tamaño de los objetos: Evita cachear objetos muy grandes o colecciones extensas.

  7. Monitorear el uso de memoria: Vigila el uso de memoria, especialmente en entornos de producción.

Dependencias

  • Microsoft.Extensions.Caching.Hybrid: Proporciona el caché híbrido subyacente
  • Nosabit.Adapters.Persistence.ICacheHybrid.Abstractions: Define las interfaces implementadas

Requisitos

  • .NET 6.0 o superior

Licencia

Este proyecto está licenciado bajo la Licencia MIT.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.2.1 124 3/23/2025
1.2.0 119 3/23/2025
1.1.0 101 3/21/2025