O cache é uma técnica de armazenamento temporário de dados frequentemente acessados para reduzir o tempo de resposta e a carga de processamento. Ele permite que as aplicações armazenem informações em memória, evitando chamadas repetidas a bancos de dados, APIs ou serviços externos, melhorando o desempenho e a escalabilidade das aplicações.

O cache híbrido no ASP.NET Core combina o cache em memória (local) com o cache distribuído (remoto), oferecendo maior resiliência e eficiência. O cache local proporciona respostas rápidas, enquanto o cache remoto garante persistência de dados entre reinicializações de aplicativos. Para mais detalhes, acesse a documentação oficial do Hybrid Cache no ASP.NET Core.

Para começar, criamos o diretório raiz do projeto com o comando:

mkdir HybridCacheAspNet

Depois, entramos no diretório e criamos o projeto Web API em .NET com o comando:

dotnet new webapi

Agora, instalaremos os pacotes NuGet necessários para o projeto:

dotnet add package Microsoft.Extensions.Caching.Hybrid --version "9.0.0-preview.7.24406.2"
dotnet add package AspNetCore.Scalar
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis

Com os pacotes instalados, vamos criar os recursos de nuvem no Azure. Primeiro, faça login no Azure:

az login

Após o login, selecione a subscription com os comandos abaixo:

az account list --output table
az account set --subscription <subscription-id>

Crie um grupo de recursos no Azure com o comando:

az group create --name rg-hybrid-cache-eastus --location eastus

Agora, criamos o serviço Azure Cache for Redis:

az redis create --name redis-hybrid-cache-eastus --resource-group rg-hybrid-cache-eastus --location eastus --sku basic --vm-size C0

Podemos iniciar o desenvolvimento do app. Primeiro, iremos criar um método que retorna dados fictícios de um banco de dados:

Task<string> GetStringFromDbAsync(CancellationToken token)
{
    return Task.FromResult(DateTime.UtcNow.ToString("dd/MM/yyyy - HH:mm:ss"));
}

Depois, criamos o método que verifica se os dados estão no cache ou no banco:

async Task<string> GetStringAsync(HybridCache cache, string key, CancellationToken token = default)
{
    return await cache.GetOrCreateAsync(
        key,
        async cancel => await GetStringFromDbAsync(cancel),
        cancellationToken: token
    );
}

O que o código acima faz: ele verifica se a chave informada (key) está presente no cache. Se os dados não estiverem no cache, o método GetStringFromDbAsync é chamado para buscar a informação. Os dados buscados são armazenados no cache e retornados.

Agora, podemos criar o endpoint que consome o cache no ASP.NET Core:

app.MapGet("/get-cached-string", async (HybridCache cache, CancellationToken token) =>
{
    string myKey = "my-key";
    string value = await GetStringAsync(cache, myKey, token);
    
    return Results.Ok(value);
})
.WithName("GetCachedString");

Vamos configurar o Hybrid Cache (cache local apenas) com o seguinte código:

builder.Services.AddHybridCache(options =>
{
    options.DefaultEntryOptions = new HybridCacheEntryOptions
    {
        LocalCacheExpiration = TimeSpan.FromSeconds(10)
    };
});

Agora, configuramos o Scalar para visualizar a API e fazer requisições de forma amigável. Leia a documentação oficial do scalar!

app.MapScalarApiReference(options =>
{
    // Fluent API
    options
        .WithTitle("Hybrid cache API")
        .WithModels(false)
        .WithDefaultHttpClient(ScalarTarget.CSharp, ScalarClient.HttpClient)
        .WithTheme(ScalarTheme.Saturn);
});

Para executar o projeto, rodamos o comando abaixo:

dotnet run

Na interface do Scalar, vamos fazer várias requisições ao endpoint /get-cached-string. Podemos observar, que o valor retornado se mantém igual para todas as requisições em uma janela de 10 segundos, devido à configuração do cache local.

Agora, vamos criar outro endpoint que retorna um objeto complexo. Primeiro, criamos as classes, depois os métodos necessários e por fim, o novo endpoint:

class MyItem
{
    public Guid Id { get; set; }
}
Task<MyItem> GetValueFromDbAsync(CancellationToken token)
{
    return Task.FromResult(new MyItem
    {
        Id = Guid.NewGuid()
    });
}
async Task<MyItem> GetValueAsync(HybridCache cache, string key, CancellationToken token = default)
{
    return await cache.GetOrCreateAsync(
        key,
        async cancel => await GetValueFromDbAsync(cancel),
        cancellationToken: token
    );
}
app.MapGet("/get-cached-item", async (HybridCache cache, CancellationToken token) =>
    {
        string myKey = "my-item";
        MyItem value = await GetValueAsync(cache, myKey, token);
    
        return Results.Ok(value);
    })
    .WithName("GetCachedItem");

Podemos refazer o teste anterior! Vamos verificar que os valores dos objetos complexos retornados se mantém iguais dentro da janela de 10 segundos

Caso duas requisições sejam feitas com mais de 10 segundos entre elas, os valores retornados serão diferentes!

Agora vamos configurar o Redis remoto no Azure. Primeiro, obtemos a connection string do Redis no portal do Azure

Depois, adicionamos a configuração no arquivo appsettings.json:

  "ConnectionStrings": {
    "RedisConnectionString": "<your-connection-string>"
  }

Com a connection string configurada, ajustamos o código do cache para utilizar o Redis:

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = 
        builder.Configuration.GetConnectionString("RedisConnectionString");
});
builder.Services.AddHybridCache(options =>
{
    options.DefaultEntryOptions = new HybridCacheEntryOptions
    {
        Expiration = TimeSpan.FromSeconds(60),
        LocalCacheExpiration = TimeSpan.FromSeconds(10)
    };
});

Com essa configuração, os valores serão mantidos no cache local por 10 segundos e no cache remoto por 60 segundos. Agora, rodamos o projeto novamente e iremos fazer novas requisições para o endpoint get-cached-string.

Em seguida, reiniciamos a aplicação e, ao realizar uma nova requisição, notamos que o valor foi mantido, pois estava armazenado no Redis remoto.

Para finalizar, deletamos os recursos de nuvem utilizados para evitar cobranças:

az group delete --resource-group rg-hybrid-cache-eastus -y

Dessa forma, implementamos o Hybrid Cache no ASP.NET Core, utilizando cache local e remoto (Redis), com endpoints que demonstram o comportamento do cache. Também configuramos o Scalar para facilitar a visualização e consulta dos endpoints.

Você já pode baixar o projeto por esse link, e não esquece de me seguir no LinkedIn!

Até a próxima, abraços!

💡
Podemos te ajudar com uma revisão 100% gratuita do seu ambiente cloud.
Share this post