In-memory Cache no C#

Caching pode melhorar significativamente o desempenho e a escalabilidade de um aplicativo, reduzindo o trabalho necessário para gerar conteúdo ou buscá-los em diversas fontes. Ele funciona melhor com dados que são alterados com pouca frequência e são caros de gerar. Caching faz uma cópia dos dados que podem ser retornados muito mais rápido quando comparados com a origem.

O ASP.NET Core dá suporte a vários caches diferentes. O cache mais simples é baseado no IMemoryCache.

IMemoryCache representa um cache armazenado na memória do servidor

Por que precisamos de cache?

Em nossos aplicativos é comum chamarmos o mesmo método repetidamente e, buscar dados no banco de dados diversas vezes. Em muitas dessas chamadas, as informações não são alteradas ou atualizadas no banco de dados. Nesses casos, podemos usar o cache para reduzir a demanda ao banco de dados pois obteremos os mesmos, diretamente da memória cache.

Algumas diretrizes para uso de cache

– O código deve sempre ter uma opção de fallback para buscar dados e não depende de um valor em cache estar disponível.
– O cache usa um recurso escasso, que é a memória do servidor, então devemos limitar o crescimento do cache:
– Não use a entrada externa como chaves de cache.
– Use expirações para limitar o crescimento do cache.
– Use SetSize, size e SizeLimit para limitar o tamanho do cache, essa atividade cabe ao desenvolvedor.

MemoryCache no C#

C# MemoryCache usa o namespaceSystem.Runtime.Caching“, portanto, para incluí-lo em seu projeto, você precisa adicionar a referência ao seu projeto.

Isso pode ser feito pelo Gerenciado de Pacotes do NuGet. Vá em Procurar e pesquise por “System.Runtime.Caching“. Assim que for encontrado, instale no projeto que for usar.

Agora que incluímos o namespace em nosso aplicativo (pode ser usado com .NET Standard 2.0 ou posterior e .NET framework 4.5 ou posterior), podemos usar MemoryCache diretamente.

Explicando cache por partes

Criação de um novo objeto MemoryCache
MemoryCache.Default: Obtém uma referência para a instância MemoryCache padrão.

ObjectCache cache = MemoryCache.Default;  

Agregando valor no cache
– Podemos usar o método cache.Add (chave, valor, cacheitempolicy) para agregar valor, aqui está um exemplo simples

cache.Add("CacheName", "Value1", null);
cache.Add("CacheName2", 0, null);

Removendo chave / valor do item de cache
– Podemos remover qualquer objeto de valor-chave de cache, usando sua chave e o método .Remove(), então suponha que se desejamos remover o item de cache com Key Name = “CacheName”, simplesmente use o código abaixo

cache.Remove("CacheName");

Obtendo o valor da chave do cache
– Você pode usar cache.Get("CacheKey") para obter valor do cache

var cacheValue = cache.Get("CacheName2")

Atualizando o valor da chave do cache
– Você pode usar o método cache.Set("key","value") para atualizar o valor do item de cache.

cache.Set("CacheName2", 1, null);

Obtendo todos os objetos no cache
– Você pode percorrer todos os pares de valores-chave usando o objeto MemoryCahe.Default.

foreach (var item in cache)
{
    Console.WriteLine("cache object key-value: "+ item.Key + "-" + item.Value);
}

CacheItemPolicy
– Representa um conjunto de detalhes de remoção e expiração de uma entrada de cache específica.

var cacheItemPolicy = new CacheItemPolicy
{
    AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(60.0),
    
};
cache.Add("CacheName3", "Expires In A Minute", cacheItemPolicy);

Usando cache como uma classe de serviços

Esta classe deve auxiliar o uso do cache da aplicação e ser simples de usar. Para isso vamos criar uma classe chamada CacheService que vai contem uma instância somente leitura de cache.

using System;
using System.Collections.Generic;
using System.Runtime.Caching;
using System.Text;

namespace UsingMemoryCache
{
    static class CacheService
    {
        private static readonly ObjectCache Cache = MemoryCache.Default;
        
    }
}

Agora podemos criar nossos métodos para adicionar e remover informações do cache. Como queremos usar este classe para salvar qualquer tipo de informação, então usaremos Generics.

using System;
using System.Collections.Generic;
using System.Runtime.Caching;
using System.Text;

namespace UsingMemoryCache
{
    static class CacheService
    {
        private static readonly ObjectCache Cache = MemoryCache.Default;

        public static T Add<T>(string key, T obj, DateTime time) where T : class
        {
            if (obj == null) //cant cache null.
            {
                return null;
            }
            Cache.Add(key, obj, time);
            return obj;
        }

        public static void Remove(string key)
        {
            Cache.Remove(key);
        }

    }
}

Uma vez que temos os valores adicionados e podemos removê-los, devemos poder buscar os dados no cache. Então vamos criar o método para buscar as informações e também para verificar se existem em cache.

using System;
using System.Collections.Generic;
using System.Runtime.Caching;
using System.Text;

namespace UsingMemoryCache
{
    static class CacheService
    {
        private static readonly ObjectCache Cache = MemoryCache.Default;

        #Region Add and Remove

        public static bool Exists(string key)
        {
            return Cache.Get(key) != null;
        }

        public static T Get<T>(string key) where T : class
        {
            try
            {
                if (Cache[key] is T)
                {
                    return (T)Cache[key];
                }
                else
                {
                    throw new ApplicationException("The cache type is not the same provided");
                }
            }
            catch
            {
                throw;
            }
        }

    }
}

Para deixa a aplicação mais simples de utilizar, podemos unificar os métodos Add e Get. Evitando o trabalho ao desenvolvedor de ter que verificar se a informação já foi adicionada anteriormente. Aproveitamos para criar um método que adiciona cache, sem precisar informar o CacheItemPolicy;

using System;
using System.Collections.Generic;
using System.Runtime.Caching;
using System.Text;

namespace UsingMemoryCache
{
    static class CacheService
    {
        private static readonly ObjectCache Cache = MemoryCache.Default;

        #Region Add and Remove

        #Region Exists and Get

        public static T GetOrAdd<T>(string key, T obj, DateTime time) where T : class
        {
            var value = Get<T>(key);
            if (value != null)
            {
                return value;
            }
            if (obj == null) //cant cache null.
            {
                return null;
            }
            Cache.Add(key, obj, time);
            return obj;
        }

        public static T GetOrAdd<T>(string key, T obj) where T : class
        {
            return GetOrAdd(
              key,
              obj,
              DateTime.Now.AddHours(1));
        }

    }
}

Testando a classe de serviços de cache

Para realizar o teste, vamos criar duas classes chamadas ServiceSection e Maintenance como mostrado abaixo.

namespace UsingMemoryCache
{
    class ServiceSection
    {
        public int Id { get; set; }
        public string Protocol { get; set; }
    }

    class Maintenance
    {
        public int Id { get; set; }
        public string Type { get; set; }
    }
}

Agora na classe Program, dentro do main, vamos adicionar o código abaixo que demonstra como é utilizado a nossa classe de serviço.

using System;
using System.Runtime.Caching;

namespace UsingMemoryCache
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                CacheService.GetOrAdd<ServiceSection>("Chave01", new ServiceSection() { Id = 1, Protocol = "123-456-6578-1" });
                CacheService.GetOrAdd<ServiceSection>("Chave02", new ServiceSection() { Id = 2, Protocol = "123-456-6578-2" });
                CacheService.GetOrAdd<ServiceSection>("Chave03", new ServiceSection() { Id = 3, Protocol = "123-456-6578-3" });

                Console.WriteLine(CacheService.Get<ServiceSection>("Chave01").Protocol);
                Console.WriteLine(CacheService.Get<Maintenance>("Chave02").Type);
                CacheService.Remove("Chave02");
                Console.WriteLine(CacheService.Exists("Chave02"));
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.Message);
            }
        }
    }
}

Conclusão

A literatura sobre cache é bem extensa e pode ser encontrada aqui neste link. Este exemplo é uma maneira simples de poder utilizar esse recurso em seu ambiente. Para ter um controle maior sobre o cache, eu recomendo a documentação oficial.

Qualquer dúvida ou dicas, entre em contato: leandrolt@gmail.com

Principais referências

– https://docs.microsoft.com/pt-br/aspnet/core/performance/caching/memory?view=aspnetcore-5.0
– https://csharp.hotexamples.com/examples/-/CacheService/-/php-cacheservice-class-examples.html
– http://www.dannejaha.se/programming/c-net/caching/Nd205ebc4_cacheservicecs/
– https://qawithexperts.com/article/c-sharp/in-memory-cache-c-explanation-with-example/302
– https://docs.microsoft.com/pt-br/dotnet/api/system.runtime.caching.memorycache?view=dotnet-plat-ext-5.0
– https://docs.microsoft.com/pt-br/dotnet/api/system.runtime.caching?view=dotnet-plat-ext-5.0

Leave a Reply

Your email address will not be published. Required fields are marked *