MemoryCache – trabalhando com cache no .net

Facebooktwittergoogle_plusredditpinterestlinkedinmail

Hoje vamos falar do MemoryCache, uma classe do .net framework que nos permite fazer cache de nossos objetos de forma simples e fácil, otimizando a performance de nossas aplicações. O MemoryCache é um cache em memória, baseado em chave e valor, de forma similar ao Redis. Porém ele tem algumas diferenças, e ele tem aplicações um pouco diferentes do Redis. O MemoryCache não é persistido, ou seja, ao derrubar a aplicação, seu caché já era, outro ponto importante, ele não é distribuído. E uma das grandes vantagens que vejo, é que ele sobe junto da sua aplicação, ele faz parte da sua aplicação, com isso não existe latência de rede, pois não é necessário fazer o acesso à outra aplicação diferente. Eu utilizo o MemoryCache em aplicações em produção, e estou bastante satisfeito com seu desempenho. Então vamos parar de conversa, e vamos ver como utilizar na prática. Crie um projeto do tipo Class Library com o nome MemoryCacheDemo, e crie um projeto do tipo Unit Test com o nome MemoryCacheTest. Adicione o assembly System.Runtime.Caching nos projetos.

Em nossa class library, renomear a classe Class1, para Desenvolvedor, e deixá-la dessa forma:

namespace MemoryCacheDemo
{
    public class Desenvolvedor
    {
        public string Nome { get; set; }
        public string Linguagem { get; set; }
        public int AnosExperiencia { get; set; }

        public override string ToString() => $"Nome: {Nome} / Linguagem: {Linguagem} / Anos Experiência: {AnosExperiencia}";
    }
}

Agora vamos à nossa classe de teste, para vermos o MemoryCache funcionando na prática. Primeiro de tudo, vamos criar o método Initialize e criar nosso objeto MemoryCache:

[TestInitialize]
public void Initialize()
{
    memoryCache = MemoryCache.Default;
}

E declare uma variável do tipo MemoryCache na classe de teste dessa forma:

private MemoryCache memoryCache;

Agora vamos inserir um objeto no cache, para isso vamos usar o método Add, da classe MemoryCache. Esse método espera como parâmetro um objeto do tipo CacheItemPolicy, que define a política de expiração da chave, além de definir delegates para quando o objeto for alterado ou removido do cache. Então crie um método chamado TestAdd com o seguinte código:

[TestMethod]
public void TestAdd()
{
    Desenvolvedor dev = new Desenvolvedor();
    dev.Nome = "Ninja";
    dev.Linguagem = "C#";
    dev.AnosExperiencia = 15;

    CacheItemPolicy policy = new CacheItemPolicy();
    policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(60);

    Assert.IsTrue(memoryCache.Add("dev", dev, policy));
}

O que fizemos acima, foi criar uma instância de Desenvolvedor, criar a política de cache com tempo de duração de 60 segundos, ou seja, após 60 segundos, a chave e seu valor irão expirar, fizemos isso usando a propriedade AbsoluteExpiration. E no final, adicionamos o objeto no cache, usando a política que definimos.

Agora vamos criar um método para testar o Get:

[TestMethod]
public void TestGet()
{
    Desenvolvedor dev = new Desenvolvedor();
    dev.Nome = "Ninja";
    dev.Linguagem = "C#";
    dev.AnosExperiencia = 15;

    CacheItemPolicy policy = new CacheItemPolicy();
    policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(60);

    memoryCache.Add("devGet", dev, policy);

    Desenvolvedor devGet = (Desenvolvedor)memoryCache["devGet"];

    Assert.AreEqual(dev.ToString(), devGet.ToString());
}

O que fizemos aqui, foi adicionar uma chave, da mesma forma que no método anterior, e depois buscar a chave usando memoryCache[“devGet”], o que também poderia ser feito usando memoryCache.Get(“devGet”). E ao final, chamamos o Assert usando o método ToString que fizemos override na classe Desenvolvedor.

Vamos agora testar o tempo de expiração, então vamos criar um novo método chamado TestGetExpired:

[TestMethod]
public void TestGetExpired()
{
    Desenvolvedor dev = new Desenvolvedor();
    dev.Nome = "Ninja";
    dev.Linguagem = "C#";
    dev.AnosExperiencia = 15;

    CacheItemPolicy policy = new CacheItemPolicy();
    policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(1);

    memoryCache.Add("devExpired", dev, policy);
    Thread.Sleep(3000);

    Desenvolvedor devGet = (Desenvolvedor)memoryCache["devExpired"];
            
    Assert.IsNull(devGet);
}

O que fizemos de diferente nesse método, foi definir um tempo de expiração de 1 segundo, e chamar Thread.Sleep(3000) para que a execução seja interrompida por 3 segundos, e depois testar se nosso valor é nulo.

Agora vamos ver como remover uma chave, então crie o método TestRemove:

[TestMethod]
public void TestRemove()
{
    Desenvolvedor dev = new Desenvolvedor();
    dev.Nome = "Ninja";
    dev.Linguagem = "C#";
    dev.AnosExperiencia = 15;

    CacheItemPolicy policy = new CacheItemPolicy();
    policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(60);

    memoryCache.Add("devRemove", dev, policy);

    Desenvolvedor devGet = (Desenvolvedor)memoryCache["devRemove"];
    Assert.AreEqual(dev.ToString(), devGet.ToString());

    memoryCache.Remove("devRemove");

    devGet = (Desenvolvedor)memoryCache["devRemove"];
    Assert.IsNull(devGet);
}

O que fizemos aqui foi adicionar uma chave ao cache, buscar a chave no cache e testar se ela retornou, e por fim, fizemos sua remoção, e novamente fizemos a busca e testamos o resultado para garantir que seja nulo.

Para controlar o tempo de expiração, nós usamos a propriedade AbsoluteExpiration da classe CacheItemPolicy, e como dito anteriormente, essa propriedade define uma data, que ao ser atingida, o objeto será removido do cache, no nosso caso, pegamos a data atual e adicionamos 60 segundos. Existe uma outra propriedade chamada SlidingExpiration, que é do tipo TimeSpan, e a diferença dela para AbsoluteExpiration, é que a SlidingExpiration define quanto tempo a chave irá permanecer no cache desde seu último acesso, ou seja, se você definir que a chave irá durar 60 segundos, e a cada 30 segundos você acessá-la, então a chave nunca irá expirar, pois a cada acesso, ela ganha mais 60 segundos de vida. Vamos ver um exemplo, para isso crie um método TestSliding:

[TestMethod]
public void TestSliding()
{
    Desenvolvedor dev = new Desenvolvedor();
    dev.Nome = "Ninja";
    dev.Linguagem = "C#";
    dev.AnosExperiencia = 15;

    CacheItemPolicy policy = new CacheItemPolicy();
    policy.SlidingExpiration = TimeSpan.FromSeconds(3);

    memoryCache.Add("devSliding", dev, policy);

    Thread.Sleep(2000);
    Desenvolvedor devGet = (Desenvolvedor)memoryCache["devSliding"];
    Assert.AreEqual(dev.ToString(), devGet.ToString());

    Thread.Sleep(2000);
    devGet = (Desenvolvedor)memoryCache["devSliding"];
    Assert.AreEqual(dev.ToString(), devGet.ToString());

    Thread.Sleep(4000);
    devGet = (Desenvolvedor)memoryCache["devSliding"];
    Assert.IsNull(devGet);
}

Veja que definimos um tempo de expiração de 3 segundos, e que fizemos um sleep de 2 segundos, acessamos a chave, depois fizemos mais um sleep de 2 segundos, acessamos a chave, que retornou com valor, mesmo tendo se passado 4 segundos após definirmos a chave no cache. Por fim, fizemos um sleep de 4 segundos, e um novo acesso à chave, que dessa vez não retornou valor, pois ficou mais de 3 segundos sem nenhum acesso.

A classe MemoryCache ainda nos oferece mais alguns recursos interessantes, como o método GetCount, que retorna a quantidade de objetos existentes no cache. Vamos usá-lo:

[TestMethod]
public void TestCount()
{
    MemoryCache memoryCount = new MemoryCache("count");

    Desenvolvedor dev = new Desenvolvedor();
    dev.Nome = "Ninja";
    dev.Linguagem = "C#";
    dev.AnosExperiencia = 15;

    CacheItemPolicy policy = new CacheItemPolicy();
    policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2);

    memoryCount.Add("devRemove", dev, policy);

    Assert.AreEqual(1, memoryCount.GetCount());
}

O MemoryCache oferece dois métodos muito úteis e importantes, são eles:
-PhysicalMemoryLimit: define o percentual máximo de memória que o MemoryCache pode utilizar;
-CacheMemoryLimit: define o total da memória em bytes, que o MemoryCache pode utilizar.

Trabalhar com cache no .net é simples e fácil, além de ser um recurso muito poderoso, abrindo uma série de possibilidades em uma aplicação, permitindo diminuir a quantidade de acessos ao banco de dados por exemplo, ou para armazenar em memória certas configurações, enfim, qualquer dado que você possa precisar armazenar para ganhar performance e agilidade.

O código completo do exemplo está disponível no GitHub: https://github.com/rdakar/memorycache-trabalhando-com-cache

 

 

Facebooktwittergoogle_plusredditpinterestlinkedinmail

1 thought on “MemoryCache – trabalhando com cache no .net

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *