Minha opinião sobre cache na memória
Tenho trabalhado em algumas coisas que lidam com uma grande quantidade de dados. Ao fazer isso, percebi que uma grande parte disso nunca muda, ou pelo menos por algum tempo fixo, isso não acontece.
Então pensei que seria útil criar um repositório de cache pessoal, claro que isso não é novidade, há algumas semanas li sobre isso no post do StackOverflow escrito por Nick Craver sobre como eles gerenciam o cache do aplicativo.
Além disso, sempre quis trabalhar com cache, como funciona, a lógica e como poderia fazê-lo funcionar, então… por que não!
Fluxo
Aqui está um fluxo rápido de como a classe que tem o controle do cache se comportará quando o usuário solicitar um valor.
Implementação
Antes de começarmos
Eu sei, eu sei. Existe System.Runtime.Caching que lida com o cache de memória. Mas eu decidi construí-lo sozinho, se você quiser usar essa classe, verifique aqui para obter instruções.
CacheItem
O primeiro passo é ter uma classe que armazene o valor de um objeto e a data de validade. Provavelmente há uma maneira melhor de fazer isso, mas foi isso que pensei, então pronto:
public class CacheItem
{
public string Identifier { get; set; }
public object Value { get; set; }
public DateTime ValidUntil { get; set; }
public CacheItem(string identifier, object value, TimeSpan valid)
{
Identifier = identifier;
Value = value;
ValidUntil = DateTime.UtcNow.Add(valid);
}
}
CacheRepositório
Então precisamos de uma classe que lide com os objetos e os salve em algum lugar (como CacheItem). Eu gosto de lidar com todos os dados/modelos em classes que possuem o sufixo Repository, mas você não precisa, então vamos construir um para armazenamento em cache.
public class CacheRepository
{
private static Dictionary<string, string> Cache = new Dictionary<string, string>();
private static T Set<T>(string key, Func<T> lookup, TimeSpan durationMinutes)
{
var item = new Models.Item(key, lookup(), durationMinutes);
return Save<T>(key, item);
}
private static T Save<T>(string key, Item item)
{
Cache[key] = JsonConvert.SerializeObject(item);
return (T)item.Value;
}
private static T Get<T>(string key)
{
var cached = Cache.FirstOrDefault(x => x.Key == key).Value;
var item = string.IsNullOrEmpty(cached) ? null : JsonConvert.DeserializeObject<Item>(cached);
return (item == null)
? default : (item.ValidUntil > DateTime.UtcNow)
? JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(item.Value)) : default;
}
public static T GetOrSet<T>(string key, Func<T> lookup, TimeSpan durationMinutes)
{
var cache = Get<T>(key);
return cache == null ? Set(key, lookup, durationMinutes) : cache;
}
}
Até agora temos um dicionário estático chamado Cache onde todos os itens serão armazenados. Lembre-se de que isso só durará enquanto o aplicativo estiver em execução, por isso este título tutorial contém cache na memória.
Tenha em mente que o item Cache será inicializado assim que a classe CacheRepository for carregada.
O único método disponível ao invocar a classe CacheRepository é GetOrSet(string key, Func<T> lookup, TimeSpan durationMinutes) que precisa de três parâmetros:
key: o identificador do objeto a ser salvo.lookup: a função de retorno de chamada caso o cache tenha expirado ou seja nulo.durationMinutes: a duração em minutos que será adicionada ao horário atual (em UTC).
Hora de armazenar em cache
Agora, use nosso repositório de cache para obter alguns dados de algum lugar.
Para que tudo isso faça sentido, vamos criar um objeto de exemplo com algumas propriedades, e depois um repositório para buscar e preencher uma lista desse objeto.
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UserRepository
{
public List<User> Get()
{
// Code to get users
}
}
Como temos o método para preencher uma lista de User, vamos usar a classe CacheRepository.
private UserRepository _userRepository = new UserRepository();
private List<User> _user;
public List<User> User
{
get
{
_user = CacheRepository.GetOrSet($"users", usersRepo.Get, TimeSpan.FromMinutes(10));
return _user;
}
}
E assim mesmo, toda vez que você acessar a variável User, ele irá pedir ao CacheRepository o valor de um objeto que possui a chave users.
Se essa chave existir, ela verificará a data de validade. Se qualquer uma dessas condições for falsa, ele usará o retorno de chamada para definir o valor (com usersRepo.Get) do objeto, salvá-lo no cache com a data de expiração definida como DateTime.UtcNow + TimeSpan.FromMinutes(10) e devolvê-lo.
