Este guia explora a utilização do mecanismo de cache em memória nativo do ASP.NET Core 3.1, demonstrando diferentes abordagens como expiração absoluta, deslizante e dependência de arquivo.
Configuração Inicial
Para habiltiar o cache em memória, registre o serviço no método ConfigureServices da sua classe Startup:
services.AddMemoryCache();
Helper de Cache Personalizado
É recomendável criar uma classe auxiliar para gerenciar as operações de cache. Abaixo, um exemplo de como encapsular a lógica de cache:
using Microsoft.Extensions.Caching.Memory;
using System;
using System.IO;
using Microsoft.Extensions.FileProviders.Physical;
public static class CacheHelper
{
private static readonly IMemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions());
private static readonly object cacheLock = new object();
private static IMemoryCache GetCacheInstance()
{
if (memoryCache == null)
{
lock (cacheLock)
{
if (memoryCache == null)
{
// Em um cenário real, pode ser necessário injetar MemoryCacheOptions
// via DI para configuração mais avançada.
return new MemoryCache(new MemoryCacheOptions());
}
}
}
return memoryCache;
}
/// <summary>
/// Define um item no cache com expiração absoluta ou deslizante.
/// </summary>
/// <typeparam name="TValue">Tipo do valor a ser cacheado.</typeparam>
/// <param name="cacheKey">A chave do cache.</param>
/// <param name="value">O valor a ser armazenado.</param>
/// <param name="absoluteMinutes">Tempo em minutos para expiração absoluta. Se 0, usa expiração deslizante.</param>
/// <param name="slidingMinutes">Tempo em minutos para expiração deslizante. Usado se absoluteMinutes for 0.</param>
public static void SetCacheItem<TValue>(string cacheKey, TValue value, int absoluteMinutes = 5, int slidingMinutes = 0)
{
using (var entry = GetCacheInstance().CreateEntry(cacheKey))
{
entry.Value = value;
if (absoluteMinutes > 0)
{
entry.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(absoluteMinutes);
}
else if (slidingMinutes > 0)
{
entry.SlidingExpiration = TimeSpan.FromMinutes(slidingMinutes);
}
}
}
/// <summary>
/// Define um item no cache com dependência de arquivo.
/// </summary>
/// <typeparam name="TValue">Tipo do valor a ser cacheado.</typeparam>
/// <param name="cacheKey">A chave do cache.</param>
/// <param name="value">O valor a ser armazenado.</param>
/// <param name="filePath">O caminho do arquivo para monitoramento.</param>
public static void SetCacheItemWithFileDependency<TValue>(string cacheKey, TValue value, string filePath)
{
if (!File.Exists(filePath))
{
// Garante que o arquivo exista para que o PollingFileChangeToken funcione.
// Em um cenário real, você pode querer lidar com isso de forma mais robusta.
File.Create(filePath).Dispose();
}
var fileInfo = new FileInfo(filePath);
using (var entry = GetCacheInstance().CreateEntry(cacheKey))
{
entry.Value = value;
entry.AddExpirationToken(new PollingFileChangeToken(fileInfo, TimeSpan.FromSeconds(1))); // Verifica a cada segundo
}
}
/// <summary>
/// Obtém um item do cache.
/// </summary>
/// <typeparam name="TValue">Tipo do valor esperado.</typeparam>
/// <param name="cacheKey">A chave do cache.</param>
/// <returns>O valor em cache ou o valor padrão do tipo se não encontrado.</returns>
public static TValue GetCacheItem<TValue>(string cacheKey)
{
if (GetCacheInstance().TryGetValue(cacheKey, out TValue cachedValue))
{
return cachedValue;
}
return default;
}
/// <summary>
/// Remove um item do cache.
/// </summary>
/// <param name="cacheKey">A chave do cache.</param>
public static void RemoveCacheItem(string cacheKey)
{
GetCacheInstance().Remove(cacheKey);
}
}
Testando Cache com Dependência de Arquivo
Abaixo está um exemplo de como usar o cache com dependência de arquivo em um endpoint de API:
using Microsoft.AspNetCore.Mvc;
using System;
[ApiController]
[Route("api/[controller]")]
public class CacheController : ControllerBase
{
[HttpGet("file-dependency")]
public IActionResult GetWithFileDependency()
{
const string cacheKey = "fileDataCache";
string cachedData = CacheHelper.GetCacheItem<string>(cacheKey);
if (string.IsNullOrEmpty(cachedData))
{
string dataToCache = $"Dados carregados em: {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
// Certifique-se de que o arquivo 'cache_monitor.txt' exista no diretório base da aplicação.
// Você pode criá-lo manualmente ou via código na inicialização.
string monitorFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache_monitor.txt");
CacheHelper.SetCacheItemWithFileDependency(cacheKey, dataToCache, monitorFilePath);
cachedData = dataToCache;
}
return Ok(new { Message = "Dados do cache com dependência de arquivo", Data = cachedData });
}
[HttpGet("absolute-cache")]
public IActionResult GetWithAbsoluteCache()
{
const string cacheKey = "absoluteTimeCache";
string cachedTime = CacheHelper.GetCacheItem<string>(cacheKey);
if (string.IsNullOrEmpty(cachedTime))
{
cachedTime = $"Tempo absoluto: {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
CacheHelper.SetCacheItem(cacheKey, cachedTime, absoluteMinutes: 1); // Cache válido por 1 minuto
}
return Ok(new { Message = "Dados do cache com expiração absoluta", Data = cachedTime });
}
[HttpGet("sliding-cache")]
public IActionResult GetWithSlidingCache()
{
const string cacheKey = "slidingTimeCache";
string cachedTime = CacheHelper.GetCacheItem<string>(cacheKey);
if (string.IsNullOrEmpty(cachedTime))
{
cachedTime = $"Tempo deslizante: {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
CacheHelper.SetCacheItem(cacheKey, cachedTime, absoluteMinutes: 0, slidingMinutes: 2); // Cache válido por 2 minutos sem atividade
}
return Ok(new { Message = "Dados do cache com expiração deslizante", Data = cachedTime });
}
}
Comportamento da Dependência de Arquivo
- Ao acessar o endpoint
/api/cache/file-dependency, os dados são carregados e armazenados em cache. - Se você solicitar o mesmo endpoint várias vezes sem modificar o arquivo
cache_monitor.txt, os dados em cache serão retornados. - Ao modificar o conteúdo do arquivo
cache_monitor.txte, em seguida, solicitar o endpoint novamente, o cache será invalidado e os dados serão recarregados, refletindo a modificação.
Exemplo de Estrutura de Configuração e Modelo
Para cenários onde você carrega configurações de arquivos JSON e deseja cacheá-las:
using Microsoft.Extensions.Configuration;
using System.IO;
public class AppConfigLoader
{
private const string ConfigCacheKey = "jwtConfigCacheKey";
public static JwtConfigModel LoadJwtConfiguration()
{
JwtConfigModel config = CacheHelper.GetCacheItem<JwtConfigModel>(ConfigCacheKey);
if (config == null)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("configs/appsettings.json") // Assumindo um arquivo de configuração
.Build();
config = configuration.GetSection("JwtSettings").Get<JwtConfigModel>();
// Cacheia a configuração carregada, talvez com dependência de arquivo se appsettings.json for monitorado.
string configFilePath = Path.Combine(Directory.GetCurrentDirectory(), "configs", "appsettings.json");
CacheHelper.SetCacheItemWithFileDependency(ConfigCacheKey, config, configFilePath);
}
return config;
}
}
public class JwtConfigModel
{
public string SecretKey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
}