Skip to content

Posts

Implementando Cache de Requisição no ASP.Net Core

8 de julho de 2019 • 4 min de leitura

Implementando Cache de Requisição no ASP.Net Core

Em algum momento no desenvolvimento de uma aplicação, geralmente bem cedo, você percebe que a aplicação está lenta. Após algumas pesquisas, o culpado é recuperar desnecessariamente os mesmos dados, e uma luz se acende, e você pensa: “Eu preciso de algum cache.”

Cache é um padrão inestimável para eliminar chamadas redundantes para um banco de dados ou uma API de terceiros. A Microsoft fornece IMemoryCache para cache baseado em tempo, no entanto, às vezes cache baseado em tempo não é o que você precisa. Neste artigo, analisamos o cache com escopo de requisição e como ele pode nos beneficiar.

O que é cache de requisição? Cache de requisição é um mecanismo para armazenar dados em cache durante a vida de uma requisição web. No dot-net, tivemos essa capacidade em alguma medida com a coleção HttpContext.Items, no entanto, HttpContext não é conhecido por sua injetabilidade.

Cache com escopo de requisição tem alguns benefícios: Primeiro, elimina a preocupação com dados obsoletos. Na maioria dos cenários, uma requisição executa em menos de um segundo, o que tipicamente não é tempo suficiente para os dados se tornarem obsoletos. E segundo, expiração não é uma preocupação porque os dados morrem quando a requisição termina.

Por padrão, o Asp.Net Core não tem cache injetável. Como mencionado anteriormente, HttpContext.Items é uma opção, mas não é uma solução elegante.

Felizmente para nós, o ASP.Net Core nos dá as ferramentas para criar uma implementação de Cache de Requisição injetável usando o framework de injeção de dependência (DI) integrado.

O framework DI integrado tem três tempos de vida para dependências: Singleton, Scoped e Transient. Singleton é para a vida da aplicação, Scoped é para a vida da requisição e Transient é uma nova instância a cada requisição.

Criei uma interface modelada após a interface IMemoryCache para manter as coisas consistentes.

Interface

public interface IRequestCache
{
    /// <summary>
    /// Add the value into request cache. If the key already exists, the value is overwritten.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="TValue"></typeparam>
    void Add<TValue>(string key, TValue value);

    /// <summary>
    /// Remove the key from the request cache
    /// </summary>
    /// <param name="key"></param>
    void Remove(string key);

    /// <summary>
    /// Retrieve the value by key, if the key is not in the cache then the add func is called
    /// adding the value to cache and returning the added value.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="add"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    TValue RetrieveOrAdd<TValue>(string key, Func<TValue> add);

    /// <summary>
    /// Retrieves the value by key. When the key does not exist the default value for the type is returned.
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    TValue Retrieve<TValue>(string key);
}

Implementação

public class RequestCache : IRequestCache
{
    IDictionary<string, object> _cache = new Dictionary<string, object>();

    /// <summary>
    /// Add the value into request cache. If the key already exists, the value is overwritten.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="TValue"></typeparam>
    public void Add<TValue>(string key, TValue value)
    {
        _cache[key] = value;
    }

    /// <summary>
    /// Remove the key from the request cache
    /// </summary>
    /// <param name="key"></param>
    public void Remove(string key)
    {
        if (_cache.ContainsKey(key))
        {
            _cache.Remove(key);
        }
    }

    /// <summary>
    /// Retrieve the value by key, if the key is not in the cache then the add func is called
    /// adding the value to cache and returning the added value.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="add"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    public TValue RetrieveOrAdd<TValue>(string key, Func<TValue> add)
    {
        if (_cache.ContainsKey(key))
        {
            return (TValue)_cache[key];
        }

        var value = add();

        _cache[key] = value;

        return value;
    }

    /// <summary>
    /// Retrieves the value by key. When the key does not exist the default value for the type is returned.
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    public TValue Retrieve<TValue>(string key)
    {
        if (_cache.ContainsKey(key))
        {
            return (TValue)_cache[key];
        }

        return default(TValue);
    }
}

Usando o framework DI do ASP.Net Core, vamos configurá-lo como Scoped.

services.AddScoped<IRequestCache, RequestCache>();

Uso

public class UserService
{
    private readonly IRequestCache _cache;
    private readonly IUserRepository _userRepository;

    public UserService(IRequestCache cache, IUserRepository userRepository)
    {
        _cache = cache;
        _userRepository = userRepository;
    }

    public User RetrieveUserById(int userId)
    {
        var buildCacheKey = UserService.BuildCacheKey(userId);

        return _cache.RetrieveOrAdd(BuildCacheKey, () => { return _userRepository.RetrieveUserBy(userId); });
    }

    public void Delete(int userId)
    {
        var buildCacheKey = UserService.BuildCacheKey(userId);

        _userRepository.Delete(userId);
        _cache.Remove(BuildCacheKey(userId));
    }

    private static string BuildCacheKey(int userId)
    {
        return $"user_{userId}";
    }
}

É isso! Cache de Requisição agora é injetável em qualquer lugar que você precisar.

Visite o Repositório Git e sinta-se à vontade para testar o código.

Autor: Chuck Conway é especialista em engenharia de software e IA Generativa. Conecte-se com ele nas redes sociais: X (@chuckconway) ou visite-o no YouTube.

↑ Voltar ao topo

Você também pode gostar