Meine Meinung zum In-Memory-Cache
Ich habe an einigen Dingen gearbeitet, die große Datenmengen verarbeiten. Dabei wurde mir klar, dass sich ein großer Teil davon nie ändert, zumindest nicht für einen bestimmten Zeitraum.
Deshalb dachte ich, dass es nützlich wäre, ein persönliches Cache-Repository zu erstellen. Das ist natürlich nicht neu. Vor ein paar Wochen habe ich darüber in StackOverflows [Beitrag](https://nickcraver.com/blog/2019/08/06/stack-overflow-how-we-do-app-caching/#in-memory--redis-cache von Nick Craver darüber gelesen, wie sie den Anwendungs-Cache verwalten.
Außerdem wollte ich schon immer mit Cache arbeiten, wie funktioniert es, die Logik und wie könnte ich es zum Laufen bringen, also… warum nicht!
Fließen
Hier ist ein kurzer Überblick darüber, wie sich die Klasse, die die Kontrolle über den Cache hat, verhält, wenn der Benutzer nach einem Wert fragt.
Implementierung
Bevor wir beginnen
Ich weiß, ich weiß. Es gibt System.Runtime.Caching, das den Speichercache verwaltet. Aber ich habe beschlossen, es selbst zu erstellen. Wenn Sie diese Klasse verwenden möchten, finden Sie eine Anleitung hier.
CacheItem
Der erste Schritt besteht darin, eine Klasse zu haben, die den Wert eines Objekts und das Ablaufdatum speichert. Wahrscheinlich gibt es einen besseren Weg, aber das habe ich mir gedacht, also los geht’s:
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);
}
}
CacheRepository
Dann brauchen wir eine Klasse, die die Objekte verarbeitet und irgendwo speichert (als CacheItem). Ich verarbeite gerne alle Daten/Modelle in Klassen mit dem Suffix Repository, aber das ist nicht nötig, also erstellen wir eines für das Caching.
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;
}
}
Bisher haben wir ein statisches Wörterbuch namens Cache, in dem alle Elemente gespeichert werden. Denken Sie daran, dass dies nur so lange dauert, wie die Anwendung ausgeführt wird. Aus diesem Grund enthält dieser Tutorial-Titel In-Memory-Caching.
Beachten Sie, dass das Element Cache initialisiert wird, sobald die Klasse CacheRepository geladen wird.
Die einzige verfügbare Methode beim Aufrufen der CacheRepository-Klasse ist GetOrSet(string key, Func<T> lookup, TimeSpan durationMinutes), die drei Parameter benötigt:
key: die Kennung des zu speichernden Objekts.lookup: die Rückruffunktion für den Fall, dass der Cache abgelaufen ist oder null ist.durationMinutes: die Dauer in Minuten, die zur aktuellen Zeit (in UTC) addiert wird.
Zeit zum Cachen
Nutzen Sie nun unser Caching-Repository, um Daten von irgendwoher abzurufen.
Damit all dies einen Sinn ergibt, erstellen wir ein Beispielobjekt mit einigen Eigenschaften und dann ein Repository, um eine Liste dieses Objekts abzurufen und zu füllen.
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
}
}
Da wir über die Methode zum Füllen einer Liste von User verfügen, verwenden wir die Klasse 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;
}
}
Und so wird jedes Mal, wenn Sie auf die Variable User zugreifen, CacheRepository nach dem Wert eines Objekts gefragt, das den Schlüssel users hat.
Wenn dieser Schlüssel vorhanden ist, wird das Ablaufdatum überprüft. Wenn eine dieser Bedingungen falsch ist, wird der Rückruf verwendet, um den Wert (mit usersRepo.Get) des Objekts festzulegen, es im Cache mit dem auf DateTime.UtcNow + TimeSpan.FromMinutes(10) festgelegten Ablaufdatum zu speichern und es zurückzugeben.
