Mon point de vue sur le cache en mémoire

· 4 min de lecture

J’ai travaillé sur des choses qui traitaient une grande quantité de données. Ce faisant, j’ai réalisé qu’une grande partie de cela ne change jamais, ou du moins pendant un certain temps, ce n’est pas le cas.

J’ai donc pensé qu’il serait utile de créer un référentiel de cache personnel, bien sûr, ce n’est pas nouveau, il y a quelques semaines, j’ai lu cela dans le post de StackOverflow écrit par Nick Craver sur la façon dont ils gèrent le cache des applications.

De plus, j’ai toujours voulu travailler avec le cache, comment ça marche, la logique et comment puis-je le faire fonctionner, alors… pourquoi pas !

Flux

Voici un aperçu rapide de la façon dont la classe qui contrôle le cache se comportera lorsque l’utilisateur demandera une valeur.

Image de Gyazo

Implémentation

Avant de commencer

Je sais, je sais. Il existe System.Runtime.Caching qui gère le cache mémoire. Mais j’ai décidé de le construire moi-même, si vous souhaitez utiliser cette classe, consultez ici pour savoir comment faire.

CacheItem

La première étape consiste à disposer d’une classe qui stocke la valeur d’un objet et la date d’expiration. Il existe probablement une meilleure façon de procéder, mais voici ce que je pensais, alors voilà :

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);
    }
}

CacheDépôt

Ensuite, nous avons besoin d’une classe qui gère les objets et les enregistre quelque part (sous le nom de CacheItem). J’aime gérer toutes les données/modèles dans les classes qui ont le suffixe Repository, mais ce n’est pas obligatoire, alors construisons-en un pour la mise en 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;
    }
}

Jusqu’à présent, nous avons un dictionnaire statique appelé Cache où tous les éléments seront stockés. N’oubliez pas que cela ne durera que pendant l’exécution de l’application, c’est pourquoi ce titre tutoriel contient une mise en cache en mémoire.

Gardez à l’esprit que l’élément Cache sera initialisé une fois la classe CacheRepository chargée.

La seule méthode disponible lors de l’appel de la classe CacheRepository est GetOrSet(string key, Func<T> lookup, TimeSpan durationMinutes) qui nécessite trois paramètres :

  1. key : l’identifiant de l’objet à sauvegarder.
  2. lookup : la fonction de rappel en cas d’expiration du cache ou s’il est nul.
  3. durationMinutes : la durée en minutes qui sera ajoutée à l’heure actuelle (en UTC).

Il est temps de mettre en cache

Maintenant, utilisez notre référentiel de mise en cache pour obtenir des données quelque part.

Pour que tout cela ait un sens, créons un exemple d’objet avec quelques propriétés, puis un référentiel pour récupérer et remplir une liste de cet objet.

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
    }
}

Puisque nous avons la méthode pour remplir une liste de User, utilisons la 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;
    }
}

Et juste comme ça, chaque fois que vous accédez à la variable User, elle demandera à CacheRepository la valeur d’un objet qui a la clé users.

Si cette clé existe, il vérifiera la date d’expiration. Si l’une de ces conditions est fausse, il utilisera ce rappel pour définir la valeur (avec usersRepo.Get) de l’objet, l’enregistrera dans le cache avec la date d’expiration définie sur DateTime.UtcNow + TimeSpan.FromMinutes(10) et le renverra.