2017-12-18 6 views
0

Ich möchte eine Klasse erstellen, um einen Wert zurückzugeben. Dieser Wert wird für 2 Minuten in einem Wörterbuchobjekt zwischengespeichert. Während dieser zwei Minuten muss ich den zwischengespeicherten Wert zurückgeben, nach diesen Minuten sollte das Wörterbuch-Cache-Objekt den Wert erneut lesen. Ich muss das Dictionary-Objekt nicht memorychache oder etwas verwenden, und ich sollte den Code in Testmethode Klasse nicht ein Windows-Formular oder WPF ausführen.Dictionary Cache mit Ablaufzeit

Ich weiß nicht, wie das Wörterbuchobjekt nach 2 Minuten in einer Testmethode abgelaufen ist.

public class CacheClass 
{ 
    public string GetValue() 
    { 
     Dictionary<int, string> Cache = new Dictionary<int, string>(); 
     string value1 = "Lina"; 
     string value2 = "Jack"; 

     return "cached value"; 
    } 
} 
+0

Ich würde einen neuen Typ (CacheItem vielleicht?) erstellen und t machen Das Wörterbuch ein 'Dictionary >'. Der Typ "CacheItem " würde das "T" (was auch immer der Wert ist) und eine "DateTime" enthalten, die verfolgt, wann der Wert dem Cache hinzugefügt wurde. Dann können Sie einfach überprüfen, ob der zwischengespeicherte Wert noch gültig ist, bevor Sie ihn zurückgeben oder feststellen, ob er neu bewertet werden muss. – itsme86

+0

Warum nicht einen MemoryCache verwenden? Microsoft hat kürzlich den MemoryCache in einen Namespace verschoben, der das Gepäck nicht mehr enthält. https://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v=vs.110).aspx – mageos

+0

Wie kann ich eine Datetime erstellen, um den zwischengespeicherten Wert zu verfolgen? –

Antwort

2
public class Cache<TKey, TValue> 
{ 
    private readonly Dictionary<TKey, CacheItem<TValue>> _cache = new Dictionary<TKey, CacheItem<TValue>>(); 

    public void Store(TKey key, TValue value, TimeSpan expiresAfter) 
    { 
     _cache[key] = new CacheItem<TValue>(value, expiresAfter); 
    } 

    public TValue Get(TKey key) 
    { 
     if (!_cache.ContainsKey(key)) return default(TValue); 
     var cached = _cache[key]; 
     if (DateTimeOffset.Now - cached.Created >= cached.ExpiresAfter) 
     { 
      _cache.Remove(key); 
      return default(TValue); 
     } 
     return cached.Value; 
    } 
} 

public class CacheItem<T> 
{ 
    public CacheItem(T value, TimeSpan expiresAfter) 
    { 
     Value = value; 
     ExpiresAfter = expiresAfter; 
    } 
    public T Value { get; } 
    internal DateTimeOffset Created { get; } = DateTimeOffset.Now; 
    internal TimeSpan ExpiresAfter { get; } 
} 

var cache = new Cache<int, string>(); 
cache.Store(1, "SomeString", TimeSpan.FromMinutes(2)); 
+0

Also wird die ** Testmethode ** etwa 2 Minuten laufen, nur um auf Ablauf zu warten? Ich würde einen 'IDateTimeService' injizieren an die Cache-Klasse –

+0

Sie möchten nicht, dass ein Komponententest für zwei Minuten ausgeführt wird.Idealerweise ist es gut, eine Abstraktion zu haben, so dass es keine starke Abhängigkeit von einer ".Now" -Eigenschaft gibt. Aber in diesem Fall würde die Einführung die Antwort drastisch verkomplizieren. Und im wirklichen Leben könnte ich das nur mit einer Cache-Dauer von <50ms und einem 'Thread.Sleep (200)' testen, so ähnlich. –

1

ich einen neuen Cache gespeicherten Elementtyp erstellen würde, die das Element wurde dem Cache hinzugefügt zusammen mit der Zeitspur des Wertes hält:

class CacheItem<T> 
{ 
    public T Value { get; } 
    public DateTime TimeAdded { get; } 
    public bool IsValid => (DateTime.UtcNow - TimeAdded) < _timeToLive; 

    private readonly TimeSpam _timeToLive = TimeSpan.FromMinutes(2); 

    public CacheItem(T value) 
    { 
     Value = value; 
     TimeAdded = DateTime.UtcNow; 
    } 
} 

Dann Dictionary<int, CacheItem<string>> Cache Sammlung ändern

public string GetValue(int key) 
{ 
    CacheItem<string> item; 
    if (_cache.TryGetValue(key, out item) && item.IsValid) 
     return item.Value; 
    return null; // Signifies no entry in cache or entry has expired. 
} 
0

Ich denke, Sie wollen mehr einfachere Sache. Laut Ihrer Erklärung konnte ich nicht herausfinden, warum Sie ein Wörterbuch oder eine Sammlung brauchen? Denn Ihre CacheClass haben nur eine GetValue Methode und der Artikel wird nicht von außen hinzugefügt.

Also, versuchen Sie so etwas, vielleicht gibt es Ihnen einen Startpunkt;

public class CacheObject 
{ 
    public string Value { get; set; } 

    public DateTime CreationDate { get; set; } 

    public CacheObject() 
    { 
     CreationDate = DateTime.Now; 
    } 
} 

public static class CacheClass 
{ 
    private static Dictionary<int,CacheObject> _cache = new Dictionary<int, CacheObject>(); 

    public static string GetValue() 
    { 
     if (_cache.Count == 0 || _cache[0].CreationDate.AddMinutes(2) < DateTime.Now) 
     { 
      var cache = new CacheObject 
      { 
       Value = GetValueFromSource() 
      }; 
      _cache[0] = cache; 
     } 
     return _cache[0].Value; 
    } 

    public static string GetValueFromSource() 
    { 
     return "Jack"; 
    } 
} 

Verwendung;

var str = CacheClass.GetValue(); 
1

Sie müssen die Datumzeit speichern, wenn sie im Cache gespeichert wurde. Auf diese Weise können Sie das Element testen, wenn es abgelaufen ist. Wenn der Artikel nicht gefunden oder abgelaufen ist? Rufen Sie die resolvefunction dafür auf.

Hier ist ein Beispiel für einen Cache, der es selbst Funktion durch den Aufruf einer Rückruf füllen kann:

public class MyCache<TKey, TValue> 
{ 
    // type to store datetime and a value 
    private struct CacheItem 
    { 
     public DateTime RetreivedTime; 
     public TValue Value; 
    } 

    // the dictionary 
    private Dictionary<TKey, CacheItem> _cache = new Dictionary<TKey, CacheItem>(); 

    private TimeSpan _timeout; 
    private Func<TKey, TValue> _resolveFunc; 

    public MyCache(TimeSpan timeout, Func<TKey, TValue> resolveFunc) 
    { 
     // store to fields 
     _timeout = timeout; 
     _resolveFunc = resolveFunc; 
    } 

    public TValue this[TKey key] 
    { 
     get 
     { 
      CacheItem valueWithDateTime; 

      // if nothing is found, you should resolve it by default 
      var getNewValue = true; 

      // lookup the key 
      if (_cache.TryGetValue(key, out valueWithDateTime)) 
       // test if the value RetreivedTime was expired? 
       getNewValue = valueWithDateTime.RetreivedTime.Add(_timeout) > DateTime.UtcNow; 

      // was it found? or expired? 
      if (getNewValue) 
      { 
       valueWithDateTime = new CacheItem { RetreivedTime = DateTime.UtcNow, Value = _resolveFunc(key) }; 
       _cache[key] = valueWithDateTime; 
      } 

      // return the value 
      return valueWithDateTime.Value; 
     } 
    } 

    // the cleanup method, you should call it sometimes... 
    public void Cleanup() 
    { 
     var currentDateTime = DateTime.UtcNow; 

     // ToArray prevents modifying an iterated collection. 
     foreach (var keyValue in _cache.ToArray()) 
      if (keyValue.Value.RetreivedTime.Add(_timeout) < currentDateTime) 
       _cache.Remove(keyValue.Key); 
    } 
} 

Beispiel für das Caching von Text-Dateien:

class Program 
{ 
    static string RetreiveFileContent(string filename) 
    { 
     if(!File.Exists(filename)) 
      return default(string); 

     return File.ReadAllText(filename); 
    } 

    static void Main(string[] args) 
    { 
     var textFileCache = new MyCache<string, string>(TimeSpan.FromMinutes(2), RetreiveFileContent); 

     var content = textFileCache["helloworld.txt"]; 

     // sometimes you need to cleanup old data. 
     textFileCache.Cleanup(); 
    } 
} 

Sie müssen einige Ausnahmebehandlung erstellen natürlich ....

Verwandte Themen